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

时间:2014-05-12 18:49:21

标签: 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>

我的班级没有申请,我看不出原因。生成的html按预期显示,其名称与ng-class表达式中的对应名称相匹配。

当我在ng-repeat之外复制(不使用$index进行命名)时,它会按预期工作。

1 个答案:

答案 0 :(得分:6)

这是因为在ngModelController的实例化期间检索了控件的名称(在其父表单上注册的名称), according to the docs 发生在预连接阶段*之前(所以还没有插值)。

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


<强> * UPDATE

$compile 上的文档是一个很好的资源,可用于了解指令的内容以及“幕后”的内容。

简单来说,有两个主要阶段:编译阶段和链接阶段。

编译阶段,正在准备模板(例如,可能需要克隆等)并使其成为Angular-aware(例如,编译指令并解析表达式并准备好被评估),但它尚未与范围绑定(因此没有任何可评估的内容)。

  

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

链接阶段进一步分为两个子阶段: 预链接 后连接< / EM>

在此阶段,范围开始起作用,插值表达式(例如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}}”)。


  5. 更新2

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

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

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

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

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

    4. 在注册控件之前,我们的指令将更新ngModelController的{​​{1}}属性(并在元素上设置名称)。

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

    6. 简单的馅饼:

      $name

      另请参阅此 short demo