2

我有一组经常重复使用的表单输入,它们在我的应用程序中重复使用,因此我试图将它们封装在自定义指令中。我想ngModel在我的指令上设置一个,并将其拆分为可在主指令中的几个不同输入(其中一些是指令本身)中进行编辑。

同时,我需要将表单验证结果向上传递到父表单,以便我可以显示适当的消息和样式。

实现这一点的最简单和最惯用的方法是什么?

这些(简化的)模板应该给你一个我要做什么的例子......

外部模板.html

<form name="outerForm">
  <my-directive
    ng-model="ctrl.myComplexModel"
    name="myDirectiveInstance"
    custom-required="ctrl.EnableValidateOne"
    toggle-another-validation="ctrl.EnableValidateTwo">
  </my-directive>
  <div ng-messages="outerForm.myDirectiveInstance.$error">
    <ng-message when="customRequired">This is required.</ng-message>
    <ng-message when="anotherValidation">This is required.</ng-message>
    <ng-message when="innerValidationOne">Something wrong with field 1.</ng-message>
    <ng-message when="innerValidationTwo">Something wrong with field 2.</ng-message>
    <ng-message when="innerValidationThree">Something wrong with field 3.</ng-message>
    <!-- etc... -->
  </div>
</form>

myDirectiveTemplate.html

<div ng-form="myDirectiveForm">
  <div ng-class="{'has-error': myDirectiveForm.fieldOne.$invalid}">
    <ui-select
      ng-model="model.fieldOne"
      name="fieldOne"
      required>
    </ui-select>
  </div>
  <div ng-class="{'has-error': myDirectiveForm.fieldTwo.$invalid}">
    <input
      type="number"
      ng-model="model.fieldTwo"
      name="fieldTwo"
      ng-pattern="directiveCtrl.someRegEx"
      ng-required="directiveCtrl.fieldTwoIsRequired">
  </div>
  <!-- etc... -->
</div>

目前,两者myDirectiveFormmyDirectiveInstance将自己发布为outerForm FormController. 我希望让这个指令成为一个黑匣子,所以myDirectiveForm直接附加到的事实outerForm困扰着我,似乎表明我做错了什么。

这是我的指令定义现在的样子。

myDirective.js

app.directive('myDirective', function() {
  return {
    restrict: 'E',
    template: 'myDirectiveTemplate.html',
    controller: 'MyDirectiveCtrl',
    scope: {
      model: '=ngModel',
      customRequired: '=?',
      toggleAnotherValidation: '=?'
    },
    require: 'ngModel',
    link: function(scope, iElem, iAttrs, ngModelController) {

      // Black-box the internal validators

      // Custom validator to avoid conflicts with ngRequired
      ngModelController.$validators.customRequired = function(modelValue, viewValue) {
        if(!scope.customRequired)
          return true;

        // On first digest the field isn't registered on the form controller yet
        if(angular.isUndefined(scope.myDirectiveForm.fieldOne))
          return true;

        return !scope.myDirectiveForm.fieldOne.$error.required;
      };

      ngModelController.$validators.anotherValidation = function(modelValue, viewValue) {
        if(!scope.anotherValidation)
          return true;

        return scope.passesBusinessRule();
      };

      ngModelController.$validators.innerValidationOne = function(modelValue, viewValue) {
        if(!scope.anotherValidation)
          return true;

        if(angular.isUndefined(scope.myDirectiveForm.fieldTwo))
          return true;

        return !scope.myDirectiveForm.fieldTwo.$error.pattern;
      };

      /* etc... */

      // Deep-watching model so that validations will trigger on updates of properties
      scope.$watch('model', function() {
        ngModelController.$validate();
      }, true);
    }
  };
});
4

2 回答 2

0

这就是我理解指令的方式。在这种情况下, ng-model 和 myDirective 都是指令。我不清楚如果你正在做

1) ng-model 上的包装器或 2) 自定义指令

因为如果你想做自定义指令,你可以传入数据,例如。

{范围:{数据:'='}

而且如果你想做一个包装器,你可能不应该传入与ngModel相关的其他属性,这意味着你仍然可以传入数据

ctrl.myComplexModel, 顺便提一句。模型对象不能分配给 ng-model,因为 ng-model 不保存对象,它只保存数据。

注意:实际上我发现了这篇文章,AngularJS - 创建一个使用 ng-model 的指令

显然,您可以传入模型, https://docs.angularjs.org/api/ng/type/ngModel.NgModelController

无论如何,这对我来说太复杂了:)如果你想做一个包装器,我觉得这个模式

  1. 传入数据
  2. “有”对象

但显然你可能正在做“是一个”对象。

于 2015-10-03T15:00:49.973 回答
0

我已经制定了一个不错的解决方案。简而言之,我已经NgModelController从我的自定义指令中删除了实现,并且我完全依赖于我的自定义指令内部指令的FormController内部form。据我所知,NgModelController它并非旨在将表单包装在自定义指令中。但是,Angular 很好地支持嵌套表单,所以这是要走的路。

我没有意识到的是,从 Angular 1.3 开始,您可以为表单动态分配名称。虽然我无法阻止“黑匣子”泄漏并将其自身附加到父表单控制器,但我至少可以控制它用于在父范围内发布自身的名称,这是可以接受的,并且与 API 非常相似由 提供ngModel

更新了以下示例。

外部模板.html

<form name="outerForm">
  <my-directive
    model="ctrl.myComplexModel"
    name="myDirectiveInstance"
    custom-required="ctrl.EnableValidateOne"
    toggle-another-validation="ctrl.EnableValidateTwo">
  </my-directive>
  <div>
    <span ng-if="outerForm.myDirectiveInstance.fieldOne.$error.required">Internal field 1 is required.</span>
    <span ng-if="outerForm.myDirectiveInstance.fieldTwo.$error.required">Internal field 2 is required.</span>
    <span ng-if="outerForm.myDirectiveInstance.fieldTwo.$error.pattern">Internal field 2 format error.</span>
    <!-- etc... -->
    <ng-messages for="outerForm.myDirectiveInstance.$error">
      <ng-message when="required">At least one required field is missing.</ng-message>
      <ng-message when="custom">
        Some directive-wide error set by validate-custom on outerForm.myDirectiveInstance.internalField
      </ng-message>
      <!-- etc... -->
    </ng-messages>
  </div>
</form>

在外部模板中,我删除了ng-model指令以支持自定义属性。该name属性仍可用于确定发布内部表单的名称。

或者,ng-model可以保留 ,并且可以使用属性form-name(对下面的隔离范围绑定进行适当更改)将自定义指令发布FormController到 parent FormController,但这可能会有些误导,因为该ng-model指令不用于除了隔离范围绑定之外的任何内容。

无论哪种方式,ng-model都不应与name此用例的属性一起使用。否则,可能会发生冲突,因为NgModelController和尝试以相同的属性名称 ( )FormController将自己发布到父FormController( )。outerFormouterForm.myDirectiveInstance

由于验证错误冒泡到父form指令,ngMessages因此可以与此自定义指令一起使用,如图所示。对于更精细的错误处理,也可以访问指令的内部字段。

myDirectiveTemplate.html

<div ng-form="{{ formName }}">
  <div ng-class="{'has-error': isInvalid('fieldOne')}">
    <ui-select
      ng-model="model.fieldOne"
      name="fieldOne"
      required>
    </ui-select>
  </div>
  <div ng-class="{'has-error': isInvalid('fieldTwo')}">
    <input
      type="number"
      ng-model="model.fieldTwo"
      name="fieldTwo"
      ng-pattern="directiveCtrl.someRegEx"
      ng-required="directiveCtrl.fieldTwoIsRequired">
  </div>
  <!-- etc... -->
  <input
    type="hidden"
    ng-model="someCalculatedValue"
    name="internalField"
    validate-custom>
</div>

指令的内部模板基本保持不变。最大的不同是ngForm现在的名称是动态设置的。

为了用 ngClass 处理这个问题,角度表达式不起作用,所以我更新了我的示例以使用一个函数来$scope代替。

最后,对于指令范围的业务规则,我使用了带有ngModel指令和name集合的隐藏输入。我将用于验证的自定义迷你指令附加到此字段。此字段上的验证错误将冒泡以供父指令使用。

myDirective.js

app.directive('myDirective', function() {
  return {
    restrict: 'E',
    template: 'myDirectiveTemplate.html',
    controller: 'MyDirectiveCtrl',
    scope: {
      model: '=',
      customRequired: '=?',
      toggleAnotherValidation: '=?',
      formName: '@name'
    },
  };
});

现在几乎所有的逻辑都从指令定义中删除了。

于 2015-10-06T20:10:08.027 回答