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

时间:2016-04-01 06:57:43

标签: angularjs

我真的很高兴"新" $ onChanges方法可以在组件的控制器中实现。但是,只有在从我的组件外部覆盖绑定变量时才会触发它,而不是(例如)将项添加到现有数组时

这是预期的行为还是错误?是否有另一种方式来监听我的输入绑定的更新,除了做$ scope。$ watch on it?

我正在使用Angular 1.5.3

1 个答案:

答案 0 :(得分: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;

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

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

$watch: function(watchExp, listener, objectEquality, prettyPrintExpression)

由于设置单向绑定的方式不是这种情况,因此总是会检查参考。

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

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

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

this.$onChanges = setType;
function setType() { 
  console.log("called");
  vm.callCounter++;
}

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

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

this.changeValueArray = function() {   
      vm.valueArray.push("tomato");
  };

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

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

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

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