使用ng-repeat with directive会导致监视缺陷

时间:2015-04-11 07:37:40

标签: javascript angularjs carousel angular-ui-bootstrap

我制作了一个新的指令,基本上是一个新的旋转木马,扩展了角度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 = undefinednewVal将是我的新数据。但是,事实并非如此。数据作为双向绑定传递给轮播和自定义指令(在每个指令的范围内使用'='运算符)。

为什么会这样?我已经对此进行了长时间的调查,这是我的发现:

  1. 如果我在我的轮播中未使用 ng-repeat,则此将起作用。在初始状态期间,oldVal将为undefinednewVal将成为我的数据。但为什么ng-repeat会导致这种情况?我已经阅读了大量关于原型继承的黄金法则的文章,其中说 ng-repeat将创建隐藏/隐藏父的新childScope,但这只发生在原始对象,我将数组传递给我的数据。
  2. 我需要在我的carousel指令中使用ng-repeat ..所以我需要知道为什么ng-repeat导致这个..任何建议?

    更新: 在Plunkr here中重现了这个问题。如您所见,oldValue始终与newValue相同(我希望oldValue在开始时未定义)

2 个答案:

答案 0 :(得分:1)

我认为你遇到的问题只是对$watch工作原理的误解。

$watch 期望以相等的值初始化。请参阅文档here。具体做法是:

  

在观察者注册范围后,监听器fn是   异步调用(通过$ evalAsync)来初始化观察者。在   在极少数情况下,这是不可取的,因为在何时调用侦听器   watchExpression的结果没有改变。检测这种情况   在监听器fn中,您可以比较newVal和oldVal。如果   这两个值是相同的(===)然后监听器被调用   初始化

换句话说,检查它们是否相等是因为检测到初始调用

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

  1. 您可以检查$watch函数中 是否相等,如果是,那么这是初始调用的初始值
  2. 或者,在link函数中的该函数之外,值是它们的初始值(因为link函数等同于post-link,这意味着scope值已经链接了)所以你可以把你的代码放在那里
  3. 分叉您的Plunker here。请注意,我已将alert移到$watch之外,且值仍然有效

    修改

    如果它不在ng-repeat中而且与Plunkr中已注释掉的代码一样设置,则会看到差异的原因是您在$timeout中添加了数据。当页面最初加载时,以下两种类型呈现为:

    1. <a1 prop="data[0]"></a1>
      • HTML看起来像是写的。 data=[]。存在指令元素,使用link调用data[0]=undefined$watch使用prop=undefined
      • 进行了调用
    2. <!-- ngRepeat: element in data track by $index -->
      • HTML只是一个评论。等待填充data。不存在指令元素,这意味着link不会被称为
    3. 在超时后向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)
        • Page现在有指令元素。现在已填写link,每个人都调用data个函数。 $watch调用prop值已关联

答案 1 :(得分:1)

当您在$watch函数中注册link时,Angular已在preLink阶段处理了绑定,因此您第一次看不到undefined执行watcher(初始化调用是oldVal和newVal可能相同的唯一时刻。如果观察者在绑定解析之前注册,则oldValue将为undefined

如果确实希望看到它,您可以覆盖compile阶段并添加自定义preLink方法(默认link为{{ 1}})。

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

另请注意,如果传递给指令的postLink是数组,则应使用divs而不是scope.$watchColleciton来检测数组元素中的更改而不是更改整个数组指针。