AngularJS 1.5.x $onChanges 不适用于单向绑定更改

AngularJS 1.5.x $onChanges Not Working with One-Way Binding Changes

我不明白为什么当我更改输入中的绑定基元时 $onChanges 没有启动。有人可以看到我做错了什么,并以简单的方式解释吗?在我无法在我的实际应用程序中工作后,我制作了一个快速测试应用程序 plunkr

angular
.module('test', [])
.component('test', {

    template: '<child application="vm.application"></child>',
    controller: 'testCtrl as vm'
})
.controller('testCtrl', function() {

    var vm = this;  

    vm.$onInit = function () {

        vm.application = {
            data: {
                name: 'Test'
            }
        }
    };
})
.component('child', {

    template: '<input type="text" ng-model="vm.application.data.name">',
    bindings: {
        application: '<'
    },
    controller: 'childCtrl as vm'
})
.controller('childCtrl', function() {

    var vm = this;

    vm.$onChanges = function (changes) {
        console.log('CHANGED: ', changes);
    };
})

$onChanges 而不是 $onChange

此外,onChange 仅在父值更改时更新,而不是子值。看看这个plunkr。请注意 console.log 仅在您输入第一个输入时触发。

$onChanges 方法不会 调用以更改对象的子属性。对象的默认更改通常在组件生命周期内遵循以下顺序:

  1. UNINITIALIZED_VALUE 到 undefined
  2. undefined{}{ someAttribute: someValue, .. }
  3. {..}undefined 如果你 delete 父作用域中的对象)

为了查看子属性,您可以使用在 1.5.8 中添加的 $doCheck 方法。它在 每个 摘要周期被调用并且不带参数。拥有权利的同时也被赋予了重大的责任。在该方法中,您将放置检测某个值是否已更新的逻辑 - 新值将已经在控制器的范围内更新,您只需要找到一种方法来确定该值与先前已知值相比是否发生了变化.

您可以在开始期望更改此特定属性之前在控制器上设置一个 previousValueOfObjectAttribute 变量(例如,当子组件 B 调用组件 A 中的 output binding 函数时,目标基于该函数对象 - 这是一个输入绑定到 B - 在 A 变化中)。如果无法预测何时会发生更改,您可以在通过 $doCheck 方法观察到任何更改后制作感兴趣的特定属性的副本。

在我的特定用例中,我没有明确检查旧值和新值,但我使用了一个承诺(存储 $q.defer().promise),目的是让我 'successfully' 观察到任何变化在 $doCheck 方法中将解决该承诺。然后我的控制器看起来像下面这样:

dn.$doCheck = function () {
  if (dn.waitForInputParam &&
      dn.waitForInputParam.promise.$$state.status === 0 &&
      dn.targetObject.targetAttribute !== false)
    dn.waitForInputParam.resolve(dn.targetObject.targetAttribute);
}

dn.listenToInputChange = function () {
  dn.waitForInputParam = $q.defer();
  dn.waitForInputParam.promise.then(dn.onInputParamChanged);
}

dn.onInputParamChanged = function (value) {
  // do stuff
  //

  // start listening again for input changes -- should be async to prevent infinite $digest loop
  setTimeout(dn.listenToInputChange, 1);
}

(w.r.t.promise.$$state.status,see this post).

对于所有其他意图和目的,观察原始数据类型的变化,您仍应使用 $onChanges。参考:https://docs.angularjs.org/guide/component

处理 $onChanges 很棘手。实际上,这就是为什么在 1.5.8 版本中他们引入了 $doCheck,类似于 Angular 2 ngDoCheck.

这样,您可以手动监听内部被监听对象的变化,不会发生在$onChanges 钩子(仅在对象的引用更改时调用)。它是同一件事,但它会在 每个摘要周期 时被调用,允许您手动检查更改(但比监视更好)。

有关详细信息,请参阅 this blog post

正如上面其他人所说,Angular 不会监视对象属性的更改,但是,您可以让 Angular 相信您的对象已通过引用更改。

为了触发 $onChanges 事件,对对象进行复制就足够了:

vm.campaign = angular.extend({}, vm.campaign);

感谢@gkalpak