【问题标题】:AngularJS 1.5.x $onChanges Not Working with One-Way Binding ChangesAngularJS 1.5.x $onChanges 不适用于单向绑定更改
【发布时间】:2016-10-04 19:19:18
【问题描述】:

我不明白为什么当我更改输入中的绑定原语时 $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);
    };
})

【问题讨论】:

标签: angularjs angularjs-components angularjs-1.5


【解决方案1】:

这是$onChanges 而不是$onChange

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

【讨论】:

  • 啊,对不起,错字了,但是如果您使用我原来的示例并将其更改为 $onChanges 它将不起作用。我在我的应用程序中正确设置了它,但它不起作用。您已将示例更改为传入原语。那么 $onChanges 是否只更新传入它的原语,如果你传入一个对象,则不会进行深入检查?
  • 很抱歉错过了最后一部分,所以 $onChanges 不是用于对孩子的绑定进行的更改以允许您在不使用双向绑定的手表的情况下告诉父母(如果需要)?我不明白,如果绑定是从父级到子级,为什么父级的更改尚未影响子级,而 $onChange 将用于允许和更新向上发生。
  • 哇,这似乎违反直觉
  • 我认为它仍在更新的事实只是 JS 对象如何工作的副作用。从文档中:'但是请注意,父范围和组件范围都引用同一个对象,因此如果您要更改组件中的对象属性或数组元素,父级仍将反映该更改。因此,一般规则应该是永远不要更改组件范围内的对象或数组属性。 docs.angularjs.org/guide/component 解释得很好。
【解决方案2】:

$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),目的是为了让我在 $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

【讨论】:

    【解决方案3】:

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

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

    更多详情请见this blog post

    【讨论】:

      【解决方案4】:

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

      为了触发 $onChanges 事件,做一个对象的 shallow 副本就足够了:

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

      感谢@gkalpak

      【讨论】:

        猜你喜欢
        • 2017-08-10
        • 2017-06-15
        • 1970-01-01
        • 1970-01-01
        • 2018-01-03
        • 1970-01-01
        • 2016-06-06
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多