我有以下代码尝试使用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
进行命名)时,它会按预期工作。
答案 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转换是安全的。
因此,在您的情况下,会发生以下情况:
ngModel
指令负责在其父表单的FormController
上注册元素,在其后链接功能中调用formCtrl.$addControl(modelCtrl);
。
FormController
使用指定控制器的$name
属性来注册控件:
form[control.$name] = control;
在ngModel
的情况下,控制器是ngModelCntroller
的实例,它的$name
属性定义如下:
function(..., $attr, ...) { ... this.$name = $attr.name;
由于控制器在预链接阶段之前被实例化,$attr.name
被绑定到非插值字符串(即“itemName {{$ index}}”)。
更新2
既然我们知道问题是什么,那么继续修复它似乎是合乎逻辑的:)
这是一个可以解决问题的实现:
不要设置name
属性,因此myForm
没有注册任何内容(我们会手动注册)。
创建一个指令,只有在根据元素的范围评估表达式后才能使用父表单FormController
注册控件(让我们调用指令later-name
)。
由于控件已通过FormController
注册到ngModelController
,因此我们的指令必须能够访问这两个控制器(通过 require
属性)。
在注册控件之前,我们的指令将更新ngModelController
的{{1}}属性(并在元素上设置名称)。
我们的指令还必须“手动”删除控件(因为我们手动添加它)。
简单的馅饼:
$name
另请参阅此 short demo 。