【问题标题】:Why doesn't AngularJS $onChanges notice the array changed?为什么 AngularJS $onChanges 没有注意到数组发生了变化?
【发布时间】:2019-05-28 12:56:15
【问题描述】:

为什么 angular 没有注意到数组发生了变化,但是如果我先将其更改为 null 呢?

我有一个 AngularJS 组件,它通过 one way 双向 (=) 数据绑定接受数组。在父级中,我有一个按钮,其 ng-click 映射到此功能:

onClickAddValue()
{ 
    this.listOfValues.push(this.valueInput);
}

其中this.listOfValues 是通过绑定传递给子组件的内容。此代码无效(子组件的视图未更新)。作为一个事先做好 AngularJS 功课的聪明 cookie,我知道这是因为 listOfValues 的值没有改变,它仍然是同一个对象引用。所以我这样做:

onClickAddValue()
{ 
    var oldList = this.listOfValues;
    this.listOfValues = [];
    for (let value of oldList)
    {
        this.listOfValues.push(value);
    }
    this.listOfValues.push(this.valueInput);
}

但这也不起作用!我也试过Object.assignangular.copy。就好像我的子组件根本没有注意到它的输入有任何变化。但显然它确实有效,因为下面的代码确实有效!

onClickAddValue()
{ 
    var oldList = this.listOfValues;
    this.listOfValues = null;
    setTimeout(
        () =>
        {
            this.listOfValues = oldList;
            this.listOfValues.push(this.valueInput);
            this.$scope.$digest();
        }
    );  
}

发生了什么事?我可以看到为什么最后一个 sn-p 有效 - listOfValues 设置为 null,ng-click 自动触发摘要和角度通知新的 null 值,因此它更新视图,然后稍后 listOfValues 被更改再次,手动触发另一个摘要,并且视图再次更新。但是为什么其他版本不起作用?为什么更改对象不同于将其更改为 null,然后 然后 更改它?

【问题讨论】:

  • 你是如何使用组件中的数组的?你在用$ngChange吗?还是您在模板中使用ng-repeat
  • @georgeawg 抱歉耽搁了。在子组件中,我正在监听 $onChanges 中绑定的 listOfValues 的更改。
  • 如果您从一开始就说您使用的是双向 (=) 绑定而不是错误地声明您使用的是单向绑定,那么这个问题会更容易解决。应该避免双向(“=”)绑定,因为它增加了一个额外的观察者并且它使迁移到 Angular 2+ 更加困难。有关详细信息,请参阅AngularJS Developer Guide - Component-based Application Architecture
  • @georgeawg 我在帖子的第一行说这是双向绑定。
  • 检查编辑。您最初的问题是“单向绑定”。我将问题编辑为“双向绑定”,以使其对其他读者有用。

标签: javascript angularjs


【解决方案1】:

我正在监听$onChanges 中绑定的listOfValues 的更改

使用Array.concat 创建一个将被$onChanges 检测到的新对象:

  onClickAddValue()
  { 
    this.listOfValues.push(this.valueInput);
    this.listOfValues = this.listOfValues.concat();
  }

$onChangesLife-Cycle Hook 仅检查 reference 更改。它不检查 contents 更改。使用Array.push 更改内容 后。使用Array.concat 方法创建一个将被更改检测器检测到的新对象。

The DEMO

angular.module("app",[])
.controller("ctrl", class {
  constructor () {
    this.listOfValues = [3,5];
  }
  onClickAddValue()
  { 
    this.listOfValues.push(this.valueInput);
    this.listOfValues = this.listOfValues.concat();
  }
})
.component("myComponent",{
  bindings: {values: "<"},
  template: `
      <fieldset>
        {{$ctrl.values}}<br>
        changes={{$ctrl.changes}}
      </fieldset>
  `,
  controller: class {
    constructor () {
      this.changes=0;
    }
    $onChanges(ch) {
      this.changes++;
      //console.log(ch);
    }
  }
})
<script src="//unpkg.com/angular/angular.js"></script>
<body ng-app="app" ng-controller="ctrl as $ctrl">
    <input ng-model="$ctrl.valueInput" /><br>
    <button ng-click="$ctrl.onClickAddValue()">Add value</button>
    <br>
    {{$ctrl.listOfValues}}
    <my-component values="$ctrl.listOfValues"></my-component>
</body>

更新

做了更多的尝试,我认为这实际上与listOfValues 绑定到具有= 绑定而不是&lt; 绑定的子组件的事实有关 - 更改为&lt;绑定让它突然起作用

$ngChanges 的文档明确指出它使用属性 ('@') 和单向 ('&lt;') 绑定进行操作。

来自文档:

生命周期钩子

  • $onChanges(changesObj) - 每当单向 (&lt;) 或插值 (@) 绑定更新时调用。 changesObj 是一个散列,其键是已更改的绑定属性的名称,值是{ currentValue, previousValue, isFirstChange() } 形式的对象。使用此钩子触发组件内的更新,例如克隆绑定值以防止外部值的意外突变。请注意,这也会在您的绑定初始化时调用。

——AngularJS Comprehensive Directive API Reference - Life-Cycle Hooks

应避免双向 ("=") 绑定,因为它添加了额外的观察者,并且使迁移到 Angular 2+ 更加困难。

有关详细信息,请参阅AngularJS Developer Guide - Component-based Application Architecture

【讨论】:

  • 就像我说的,我知道您需要更改实际的对象引用——这就是为什么我在 OP 中使用 for 循环尝试了所有第二个 sn-p。它仍然不起作用(Array.concat 也不起作用)。在做了更多的尝试之后,我认为这实际上与 listOfValues 绑定到具有 = 绑定而不是 &lt; 绑定的子组件这一事实有关 - 更改为 &lt; 绑定使其突然工作。
  • @JackM $ngChanges 的文档明确指出它使用属性 ('@') 和单向 ('&lt;') 绑定进行操作。查看答案的更新。
猜你喜欢
  • 2012-04-22
  • 1970-01-01
  • 1970-01-01
  • 2016-10-18
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-12-24
相关资源
最近更新 更多