触发$ onChanges以更新单向绑定 [英] triggering $onChanges for updated one way binding

查看:104
本文介绍了触发$ onChanges以更新单向绑定的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I'm really happy with the "new" $onChanges method you can implement in a component's controller. However it only seems to be triggered when the bound variable is overwritten from outside my component, not (for instance) when an item is added to an existing array

这是预期的行为还是错误?除了执行$ scope.$ watch之外,还有其他方法可以监听我的输入绑定的更新吗?

It this intended behaviour or a bug? Is there another way of listening to updates to my input bindings, besides doing a $scope.$watch on it?

我正在使用Angular 1.5.3

I'm using Angular 1.5.3

推荐答案

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

First TL;DR For an array that is bounded via one-way binding, a watch expression is added that does not check for object equality but uses reference checking. This means that adding an element to the array will never fire the '$onChanges' method, since the watcher will never be 'dirty'.

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

I've created a plnkr that demonstrates this: http://plnkr.co/edit/25pdLE?p=preview

单击在外部添加蔬菜"和在外部添加更改数组引用",然后查看"$ onChanges调用数".只能通过后一个按钮更改.

Click the 'add vegetable in outer' and 'change array reference in outer' and look at the 'Number of $onChanges invocation'. It will only change with the latter button.

完整的说明 为了完全掌握发生了什么,我们应该检查角度代码库.当<"找到绑定后,以下代码用于设置监视表达式.

Complete explanation To fully grasp what is going on, we should check the angular code base. When a '<' binding is found, the following code is used to set up a watch expression.

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'方法.这将记录一个"$ onChanges"回调任务,该任务将在"$ postDigest"阶段执行,并通知所有侦听"$ onChanges"生命周期挂钩的组件以告知其值是否已更改.

The important part here is how the 'scope.$watch' expression is set up. The only parameters passed are the parsed expression and the listener function. The listener function is fired once the '$watch' is found dirty in the digest cycle. If it is fired, the listener will execute the 'recordChanges' method. This records an '$onChanges' callback task that will be executed in the '$postDigest' phase and notify all components that are listening for the '$onChanges' lifecycle hook to tell them if the value has changed.

在这里要记住的重要一点是,如果'$ watcher'从未被弄脏,则不会触发'$ onChanges'回调.但是更重要的是,通过创建"$ watch"表达式,除非引用发生更改,否则它永远不会弄脏.如果要检查对象之间的相等性而不是引用,则应传递一个额外的第三个参数,要求这样做:

What's important to keep in mind here, if the '$watcher' is never dirty, the '$onChanges' callback is not triggered. But even more importantly, by the way the '$watch' expression is created, it will NEVER be dirty, UNLESS the reference changes. If you wanted to check for equality between objects instead of reference, you should pass an extra third parameter that asks for this:

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

由于这里不是建立单向绑定的方式,因此它将始终进行检查以供参考.

As this is not the case here with the way the one way binding is set up, it will ALWAYS check for reference.

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

This means, if you add an element to an array, the reference is not changed. Meaning the '$watcher' will never be dirty, meaning the '$onChanges' method will not be called for changes to the array.

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

To demonstrate this, I've created a plnkr: http://plnkr.co/edit/25pdLE?p=preview

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

It contains two components, outer and inner. Outer has primitive string value that can be changed through an input box and an array that can be extended by adding an element or have its reference changed.
Inner has two one-way bounded variables, the value and the array. It listens for all changes.

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

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

If you type into the input field, the '$onChanges' callback is fired every time. This is logical and expected, since a string is primitive so it cannot be compared by reference, meaning the '$watcher' will be dirty, and the '$onChanges' lifecycle hook fired.

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

If you click the 'Add vegetable in outer', it will execute the following code:

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

在这里,我们只是将一个值添加到现有的有界数组中.我们在这里通过引用进行工作,因此不会触发'$ watcher'并且没有回调.您不会在控制台中看到计数器增量或被调用"语句.

Here we just add a value to the existing bounded array. We're working by reference here, so the '$watcher' is not fired and there is no callback. You will not see the counter increment or the 'called' statement in your console.

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

Note: If you click the 'Add something to the array' inside the inner component, the array in outer component also changes. This is logical, since we are updating the exact same array by reference. So even though it is a one-way binding, the array can be updated from inside the inner component.

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

If you change the reference in the outer component by clicking 'Change array reference in outer', the '$onChanges' callback is fired as expected.

回答您的问题:这是预期的行为还是错误?我想这是预期的行为.否则,他们会为您提供定义<"的选项以一种检查对象是否相等的方式进行绑定.您总是可以在github上创建一个问题,然后问是否愿意.

As to answer your question: Is this intended behaviour or a bug? I guess this is intended behaviour. Otherwise they would have given you the option to define your '<' binding in a way that it would check for object equality. You can always create an issue on github and just ask the question if you'd like.

这篇关于触发$ onChanges以更新单向绑定的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆