触发$ onChanges以获得更新的单向绑定

jor*_*ong 10 angularjs

我非常满意你可以在组件的控制器中实现的"新"$ onChanges方法.但是,当从我的组件外部覆盖绑定变量时,似乎只会触发它,而不是(例如)将项添加到现有数组时

这是预期的行为还是一个错误?除了做一个$ scope之外,还有另一种方法可以监听输入绑定的更新.$ watch on it?

我正在使用Angular 1.5.3

Kwi*_*enP 16

第一个TL; DR 对于通过单向绑定限制的数组,添加了一个不检查对象相等但使用引用检查的监视表达式.这意味着向数组中添加元素永远不会触发'$ onChanges'方法,因为观察者永远不会"脏".

我创建了一个演示这个的plnkr:http://plnkr.co/edit/25pdLE?p = preview

单击"在外部添加蔬菜"和"在外部更改数组引用",然后查看"$ onChanges调用数".它只会随后一个按钮而改变.

完整的解释 为了完全掌握正在发生的事情,我们应该检查角度代码库.找到'<'绑定时,以下代码用于设置监视表达式.

case '<':
        if (!hasOwnProperty.call(attrs, attrName)) {
          if (optional) break;
          attrs[attrName] = void 0;
        }
        if (optional && !attrs[attrName]) break;

        parentGet = $parse(attrs[attrName]);

        destination[scopeName] = parentGet(scope);
// IMPORTANT PART //
        removeWatch = scope.$watch(parentGet, function        parentValueWatchAction(newParentValue) {
          var oldValue = destination[scopeName];
          recordChanges(scopeName, newParentValue, oldValue);
          destination[scopeName] = newParentValue;
        }, parentGet.literal);
// ------------- //
        removeWatchCollection.push(removeWatch);
        break;
Run Code Online (Sandbox Code Playgroud)

这里的重要部分是如何设置'scope.$ watch'表达式.传递的唯一参数是解析表达式和侦听器函数.一旦在摘要周期中发现'$ watch'为脏,就会触发监听器功能.如果它被触发,则侦听器将执行'recordChanges'方法.这将记录将在'$ postDigest'阶段执行的'$ onChanges'回调任务,并通知所有正在侦听'$ onChanges'生命周期钩子的组件,告诉他们值是否已更改.

这里要记住的重要一点是,如果'$ watcher'从不变脏,则不会触发'$ onChanges'回调.但更重要的是,通过'$ watch'表达式的创建方式,除非引用发生变化,否则它永远不会变脏.如果你想检查对象之间的相等而不是引用,你应该传递一个额外的第三个参数,询问:

$watch: function(watchExp, listener, objectEquality, prettyPrintExpression)
Run Code Online (Sandbox Code Playgroud)

由于这种情况并非如此,单向绑定的设置方式,它总是会检查参考.

这意味着,如果向数组添加元素,则不会更改引用.这意味着'$ watcher'永远不会变脏,这意味着不会调用'$ onChanges'方法来更改数组.

为了证明这一点,我创建了一个plnkr:http://plnkr.co/edit/25pdLE?p = preview

它包含两个组件,外部和内部.外部具有原始字符串值,可以通过输入框和可以通过添加元素或更改其引用来扩展的数组进行更改.
Inner有两个单向有界变量,值和数组.它会倾听所有变化.

this.$onChanges = setType;
function setType() { 
  console.log("called");
  vm.callCounter++;
}
Run Code Online (Sandbox Code Playgroud)

如果输入输入字段,每次都会触发'$ onChanges'回调.这是合乎逻辑的和预期的,因为字符串是原始的,因此无法通过引用进行比较,这意味着'$ watcher'将是脏的,并且'$ onChanges'生命周期钩子被触发.

如果单击"在外部添加蔬菜",它将执行以下代码:

this.changeValueArray = function() {   
      vm.valueArray.push("tomato");
  };
Run Code Online (Sandbox Code Playgroud)

这里我们只是为现有的有界数组添加一个值.我们在这里参考工作,所以'$ watcher'没有被解雇,也没有回调.您不会在控制台中看到计数器增量或"被调用"语句.

注意:如果单击内部组件内的"向数组添加内容",则外部组件中的数组也会更改.这是合乎逻辑的,因为我们通过引用更新完全相同的数组.因此即使它是单向绑定,也可以从内部组件内部更新数组.

如果通过单击"在外部更改数组引用"来更改外部组件中的引用,则会按预期触发"$ onChanges"回调.

至于回答你的问题:这是预期的行为还是错误?我想这是预期的行为.否则,他们会给你选择以检查对象相等性的方式定义你的'<'绑定.您总是可以在github上创建一个问题,如果您愿意,可以询问问题.