使用插值名称注册表单控件

aw0*_*w04 2 angularjs

我有以下代码尝试使用一些验证样式ng-class.

<tr ng-repeat="item in items">
  <td>
    <select name="itemName{{$index}}" ng-model="item.name" ng-options="o for o in nameOptions"
      ng-class="{ 'custom-error': myForm.itemName{{$index}}.$invalid && !myForm.itemName{{$index}}.$pristine }"
      required>
        <option value="">SELECT</option>
    </select>
  </td>
</tr>
Run Code Online (Sandbox Code Playgroud)

我的班级没有申请,我不明白为什么.生成的html看起来与预期的一样,名称与ng-class表达式中的对应项匹配.

当我在一个ng-repeat(不$index用于命名)之外复制它时,它按预期工作.

gka*_*pak 6

发生这种情况是因为控件的名称(在其父表单上注册的名称)在ngModelController实例化期间被检索,根据文档在预链接阶段*之前发生(因此还没有插值).

如果你检查了它myForm的属性,你会发现它确实有一个带有键"itemName {{$ index}}"的属性.


*更新

文档$compile是一个很好的资源,用于理解指令的内容以及"幕后"的内容.

简而言之,有两个主要阶段:编译阶段和链接阶段.

编译阶段,正在准备模板(例如,它可能需要克隆等)并使其成为Angular-aware(例如,编译指令并解析表达式并准备好进行评估),但它不受约束范围(因此无需评估).

编译函数处理转换模板DOM.由于大多数指令不进行模板转换,因此不经常使用.需要编译函数的示例是转换模板DOM的指令,例如ngRepeat,或者异步加载内容,例如ngView.

连接相被进一步分为两个子阶段:预连接联交.

在此阶段,范围开始起作用,name可以根据范围的属性/函数评估插值表达式(例如属性).

link函数负责注册DOM侦听器以及更新DOM.它在克隆模板后执行.这是大多数指令逻辑的用武之地.

预链接功能
在链接子元素之前执行.由于编译器链接功能无法找到正确的链接元素,因此进行DOM转换是不安全的.

链接后功能链接 子元素后执行.在链接后功能中进行DOM转换是安全的.


所以,在你的情况下,这是发生的事情:

  1. ngModel指令负责在其父表单上注册元素FormController,并formCtrl.$addControl(modelCtrl);在其后链接函数中调用.

  2. FormController使用指定的控制器的$name性能注册控件:
    form[control.$name] = control;

  3. ngModel控制器是一个实例的情况下ngModelCntroller,它的$name属性定义如下:
    function(..., $attr, ...) { ... this.$name = $attr.name;

  4. 由于控制器在预链接阶段之前被实例化,$attr.name因此绑定到非插值字符串(即"itemName {{$ index}}").


更新2

既然我们知道问题是什么,那么继续修复它似乎是合乎逻辑的:)

这是一个可以解决问题的实现:

  1. 不要设置name属性,因此没有注册任何内容myForm(我们将负责手动注册).

  2. 创建一个指令,FormController只有在根据元素的作用域计算表达式后才能使用父表单注册控件(让我们调用指令later-name).

  3. 由于控件是FormController通过它们注册的ngModelController,因此我们的指令必须能够访问这两个控制器(通过其require属性).

  4. 注册控制之前,我们的指令将更新ngModelController$name属性(在元素上设置的名称).

  5. 我们的指令还必须"手动"删除控件(因为我们手动添加它).

非常简单:

<select later-name="itemName{{$index}}"                  <!-- (1) -->

app.directive('laterName', function () {                   // (2)
    return {
        restrict: 'A',
        require: ['?ngModel', '^?form'],                   // (3)
        link: function postLink(scope, elem, attrs, ctrls) {
            attrs.$set('name', attrs.laterName);

            var modelCtrl = ctrls[0];                      // (3)
            var formCtrl  = ctrls[1];                      // (3)
            if (modelCtrl && formCtrl) {
                modelCtrl.$name = attrs.name;              // (4)
                formCtrl.$addControl(modelCtrl);           // (2)
                scope.$on('$destroy', function () {
                    formCtrl.$removeControl(modelCtrl);    // (5)
                });
            }            
        }
    };
});
Run Code Online (Sandbox Code Playgroud)

另见这个简短的演示.