Angular 2:如何检测数组中的变化?(@input 属性)

IT技术 javascript angular typescript
2021-01-31 20:05:15

我有一个使用 ajax 请求检索对象数组的父组件。

该组件有两个子组件:一个以树结构显示对象,另一个以表格格式呈现其内容。父级通过 @input 属性将数组传递给他们的子级,他们会正确显示内容。一切都如预期。

当您更改对象中的某些字段时会出现问题:子组件不会收到这些更改的通知。仅当您手动将数组重新分配给其变量时才会触发更改。

我习惯使用 Knockout JS,我需要获得类似于 observableArrays 的效果。

我读过一些关于 DoCheck 的内容,但我不确定它是如何工作的。

6个回答

OnChanges 只有当输入属性的实例发生变化时,生命周期钩子才会触发。

如果要检查输入数组中的某个元素是否已添加、移动或删除,可以Lifecycle Hook 中使用IterableDiffersDoCheck,如下所示:

constructor(private iterableDiffers: IterableDiffers) {
    this.iterableDiffer = iterableDiffers.find([]).create(null);
}

ngDoCheck() {
    let changes = this.iterableDiffer.diff(this.inputArray);
    if (changes) {
        console.log('Changes detected!');
    }
}

如果需要检测数组内对象的变化,则需要遍历所有元素,并为每个元素应用KeyValueDiffers(您可以与之前的检查并行执行此操作)。

访问这篇文章了解更多信息:在 Angular2 中检测数组内对象的变化

oop。我刚刚和我的验光师预约了新眼镜。
2021-03-12 20:05:15
ngDoCheck资源昂贵,因为我将它与 find 方法一起使用,但@SeidMehmedovic 所做的方式是性能最好的方式。竖起大拇指
2021-03-14 20:05:15
@Sparafusile 您的链接指向IterableDiffers,而不是IterableDiffer接口。请检查:angular.io/api/core/IterableDiffer
2021-03-15 20:05:15
记得像这样初始化 iterableDiffer iterableDiffer:IterableDiffer<YourSingleItemType>;
2021-03-18 20:05:15
diff方法已重命名为findangular.io/api/core/IterableDiffers
2021-03-23 20:05:15

您始终可以通过将数组与空数组合并来创建对该数组的新引用:

this.yourArray = [{...}, {...}, {...}];
this.yourArray[0].yourModifiedField = "whatever";

this.yourArray = [].concat(this.yourArray);

上面的代码将更改数组引用,并触发子组件中的 OnChanges 机制。

你的回答拯救了我的一天
2021-03-15 20:05:15
@ErnestoAlfonso:这是一个零性能操作,因为您不克隆数组内容(元素)。您只需创建一个新变量,该变量指向与前一个相同的内存区域。
2021-03-17 20:05:15
这从性能和内存消耗的角度来看如何?
2021-03-19 20:05:15
在我的情况下,这对我来说非常有效。<dhi-context-menu [selectedJobs]="[].concat(selectedJobs$ | async)"></dhi-context-menu>
2021-03-27 20:05:15
@WojtekMajerski:“您只能创建一个指向与前一个相同内存区域的新变量”-它不能指向同一区域(这就是为什么它们首先被认为是不同的),Array.concat总是创建一个新实例,所以对初始数组元素的所有引用都被复制(浅拷贝)。完成后var x = [].concat(y),您可以分别推送到xy,它们是独立的。如果您y之后不使用,if 将被 GC 收集。
2021-04-08 20:05:15

阅读以下文章,不要错过可变对象和不可变对象。

关键问题是你改变了数组元素,而数组引用保持不变。Angular2 更改检测仅检查数组引用以检测更改。在了解了不可变对象的概念后,您就会明白为什么会遇到问题以及如何解决它。

我在我的一个项目中使用 redux store 来避免此类问题。

https://blog.thoughtram.io/angular/2016/02/22/angular-2-change-detection-explained.html

您可以使用IterableDiffers

它被*ngFor 使用

constructor(private _differs: IterableDiffers) {}

ngOnChanges(changes: SimpleChanges): void {
  if (!this._differ && value) {
     this._differ = this._differs.find(value).create(this.ngForTrackBy);
  }
}

ngDoCheck(): void {
  if (this._differ) {
    const changes = this._differ.diff(this.ngForOf);
    if (changes) this._applyChanges(changes);
  }
}

这似乎已经回答了。然而,对于未来的问题寻求者,我想添加一些在我研究和调试我遇到的变更检测问题时遗漏的东西。现在,我的问题有点孤立,无可否认,这是我的一个愚蠢的错误,但仍然相关。当您更新参考ArrayObject参考中的值时,请确保您处于正确的范围内。我通过使用将自己置于陷阱中setInterval(myService.function, 1000),在那里myService.function()将更新我在服务之外使用的公共数组的值。这实际上从未更新数组,因为绑定已关闭,正确的用法应该是setInterval(myService.function.bind(this), 1000). 我浪费了时间尝试更改检测技巧,当时这是一个愚蠢/简单的错误。在尝试变更检测解决方案之前消除范围作为罪魁祸首;它可能会为您节省一些时间。

你让我“使用 setInterval(myService.function, 1000) 将自己陷入陷阱”
2021-04-08 20:05:15