我制作了一个新指令,它基本上是一个新的转盘,扩展了有角度的UI转盘引导程序。这个新的转盘将在一帧中显示多个div。我的新指令接受数组中的任何数据,并为每个数据提供一个自定义的html模板。
然而,如果我在指令中使用旋转木马,我会在指令中看到一种奇怪的行为。一切都很好,但我指令中的手表总是为newVal
和oldVal
获得相同的值。我的意思是,这是我的转盘代码:
<slide ng-repeat="frame in ctrl.frames">
<div ng-repeat="divs in frame.divs">
<custom-directive data="divs"></custom-directive>
</div>
</slide>
在我的customDirective
控制器中,我观察数据的变化,如下所示:
$scope.$watch("ctrl.data", function(newVal, oldVal){
if (newVal !== oldVal) {
// data is updated, redraw the directive in my case
// however newVal is always the same as oldVal
}
})
newVal和oldVal总是一样的。。我期望初始状态是oldVal = undefined
,而newVal
将是我的新数据。然而,事实并非如此。数据作为双向绑定传递到转盘和自定义指令(在每个指令的作用域内使用"="运算符)。
为什么会发生这种情况我对此已经调查了很长时间,以下是我的发现:
- 如果我不在旋转木马中使用
ng-repeat
,此将有效。oldVal
将是undefined
,而newVal
将是我在初始状态期间的数据。但为什么ng重复会导致这种情况呢?我读过很多关于原型继承的黄金法则的文章,其中说ng repeat将创建新的childScope来隐藏/隐藏父对象,但这种情况只发生在原始对象组传递给我的数据
我需要在我的转盘指令中使用ng repeat。。所以我需要知道为什么ng重复会导致这种情况。。有什么建议吗?
更新:在这里重现了Plunkr中的问题。正如您所看到的,oldValue总是与newValue相同(我希望oldValue在一开始是未定义的)
当您在link
函数中注册$watch
时,Angular已经在preLink
阶段处理了绑定,因此,在第一次执行观察程序时,您永远不会看到undefined
(初始化调用是oldVal和newVal可能相同的唯一时刻。如果观察程序在绑定解析之前注册了,则oldValue将为undefined
)
如果真的想要查看它,可以覆盖compile
阶段并添加一个自定义的preLink
方法(默认的link
是postLink
)。
但我真的怀疑你是否想那样做。为什么第一次没有定义是个问题?你应该试着解释你面临的真正问题。
此外,请注意,如果传递给指令的divs
是一个数组,则应使用scope.$watchColleciton
而不是scope.$watch
来检测数组元素的变化,而不是整个数组指针的变化。
我认为您遇到的问题只是对$watch
工作方式的误解。
$watch
被期望以相等的值初始化。请参阅此处的文档。具体而言:
在向作用域注册观察程序后,侦听器fn异步调用(通过$evalAsync)以初始化观察程序。在里面在极少数情况下,这是不可取的,因为当watchExpression的结果没有更改。检测此场景在监听器fn中,您可以比较newVal和oldVal。如果这两个值是相同的(==),则侦听器被调用初始化
换句话说,你检查它们是否相等,这样你就不会检测到初始调用
在你提供的Plunker中,如果你需要做一些初始化代码,你可以做两件事:
- 您可以在
$watch
函数中检查它们是否相等,如果相等,则这是具有初始值的初始调用 - 或者,在
link
函数中的该函数之外,这些值是它们的初始值(因为link
函数等效于post-link
,这意味着scope
值已经链接),所以您可以将代码放在那里
将您的Plunker叉在此处。注意,我将alert
移到了$watch
之外,该值仍然是有效的
编辑:
当它不在ng-repeat
中并且设置为像Plunkr中注释掉的代码时,您看到差异的原因是您在$timeout
中添加了数据。当页面最初加载时,以下是两种类型呈现的内容:
<a1 prop="data[0]"></a1>
- HTML的外观与编写时一样。CCD_ 34。存在指令元素,使用
data[0]=undefined
调用link
。用prop=undefined
调用$watch
- HTML的外观与编写时一样。CCD_ 34。存在指令元素,使用
<!-- ngRepeat: element in data track by $index -->
- HTML只是一个注释。等待CCD_ 40被填充。不存在指令元素,这意味着未调用
link
- HTML只是一个注释。等待CCD_ 40被填充。不存在指令元素,这意味着未调用
当您在超时后向data
添加项目时,它们看起来像这样:
<a1 prop="data[0]"></a1>
- 同上。
data[0]
现在已定义,因此prop
已定义
- 同上。
<div ng-repeat="element in data track by $index" class="ng-scope"> <a1 prop="element" class="ng-isolate-scope"></a1> </div>
(x3)- 页面现在有指令元素。调用每个上的CCD_ 47函数,此时CCD_。使用链接的
prop
值调用$watch
- 页面现在有指令元素。调用每个上的CCD_ 47函数,此时CCD_。使用链接的