将ng repeat与指令一起使用会导致手表出现缺陷



我制作了一个新指令,它基本上是一个新的转盘,扩展了有角度的UI转盘引导程序。这个新的转盘将在一帧中显示多个div。我的新指令接受数组中的任何数据,并为每个数据提供一个自定义的html模板。

然而,如果我在指令中使用旋转木马,我会在指令中看到一种奇怪的行为。一切都很好,但我指令中的手表总是为newValoldVal获得相同的值。我的意思是,这是我的转盘代码:

<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将是我的新数据。然而,事实并非如此。数据作为双向绑定传递到转盘和自定义指令(在每个指令的作用域内使用"="运算符)。

为什么会发生这种情况我对此已经调查了很长时间,以下是我的发现:

  1. 如果我不在旋转木马中使用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方法(默认的linkpostLink)。

但我真的怀疑你是否想那样做。为什么第一次没有定义是个问题?你应该试着解释你面临的真正问题。

此外,请注意,如果传递给指令的divs是一个数组,则应使用scope.$watchColleciton而不是scope.$watch来检测数组元素的变化,而不是整个数组指针的变化。

我认为您遇到的问题只是对$watch工作方式的误解。

$watch期望以相等的值初始化。请参阅此处的文档。具体而言:

在向作用域注册观察程序后,侦听器fn异步调用(通过$evalAsync)以初始化观察程序。在里面在极少数情况下,这是不可取的,因为当watchExpression的结果没有更改。检测此场景在监听器fn中,您可以比较newVal和oldVal。如果这两个值是相同的(==),则侦听器被调用初始化

换句话说,你检查它们是否相等,这样你就不会检测到初始调用

在你提供的Plunker中,如果你需要做一些初始化代码,你可以做两件事:

  1. 您可以在$watch函数中检查它们是否相等,如果相等,则这是具有初始值的初始调用
  2. 或者,在link函数中的该函数之外,这些值是它们的初始值(因为link函数等效于post-link,这意味着scope值已经链接),所以您可以将代码放在那里

将您的Plunker叉在此处。注意,我将alert移到了$watch之外,该值仍然是有效的

编辑:

当它不在ng-repeat中并且设置为像Plunkr中注释掉的代码时,您看到差异的原因是您在$timeout中添加了数据。当页面最初加载时,以下是两种类型呈现的内容:

  1. <a1 prop="data[0]"></a1>
    • HTML的外观与编写时一样。CCD_ 34。存在指令元素,使用data[0]=undefined调用link。用prop=undefined调用$watch
  2. <!-- ngRepeat: element in data track by $index -->
    • HTML只是一个注释。等待CCD_ 40被填充。不存在指令元素,这意味着未调用link

当您在超时后向data添加项目时,它们看起来像这样:

  1. <a1 prop="data[0]"></a1>
    • 同上。data[0]现在已定义,因此prop已定义
  2. <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

最新更新