角度2:如何检测阵列中的变化?(@input属性)


74

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

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

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

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

我已经阅读了有关DoCheck的内容,但不确定如何运行。


7
@Adam不要那么浅
Renaud

Answers:


105

OnChanges 仅当输入属性的实例更改时,生命周期挂钩才会触发。

如果你想检查是否输入数组中的元素已经被添加,移动或删除,则可以使用IterableDiffers里面DoCheck的生命周期挂钩,如下所示:

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中数组内部对象的更改


1
diff方法已重命名为findangular.io/api/core/IterableDiffers
Sparafusile

4
@Sparafusile您的链接指向IterableDiffers,而不是IterableDiffer接口。请检查:angular.io/api/core/IterableDiffer
seidme

3
哎呀。我刚刚和我的视光师预约了新眼镜。
Sparafusile '18年

4
记住要像这样初始化iterableDifferiterableDiffer:IterableDiffer<YourSingleItemType>;
朱利安·托雷格罗萨

3
ngDoCheck资源昂贵,因为我将它与find方法一起使用,但是@SeidMehmedovic的方式是性能最好的方法。竖起大拇指
WasiF

21

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

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

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

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


在我的情况下,这对我来说效果很好。<dhi-context-menu [selectedJobs] =“ []。concat(selectedJobs $ | async)”> </ dhi-context-menu>
Jared Christensen

从性能和内存消耗的角度来看如何?
埃内斯托·阿方索

@ErnestoAlfonso:这是零性能操作,因为您没有克隆数组内容(元素)。您只能创建一个新变量,该变量指向与上一个相同的存储区。
Wojtek Majerski '20

您的答案挽救了我的一天
stringnome '20

11

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

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

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

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


8

您可以使用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);
  }
}

3

这已经出现了答案。但是对于将来的问题寻求者,我想补充一些我在研究和调试遇到的变更检测问题时错过的东西。现在,我的问题有点孤立,虽然我承认这是一个愚蠢的错误,但仍然有意义。当您更新ArrayObject参考中的值时,请确保您位于正确的范围内。我通过使用设置了自己的陷阱setInterval(myService.function, 1000),该位置myService.function()将更新我在服务之外使用的公共数组的值。由于绑定已关闭,因此它从未真正更新过数组,而正确的用法应该是setInterval(myService.function.bind(this), 1000)。当这是一个愚蠢/简单的错误时,我浪费了时间尝试更改检测黑客。在尝试使用变更检测解决方案之前,应将范围视为罪魁祸首;可能会节省您一些时间。


您让我“通过使用setInterval(myService.function,1000)将自己置入陷阱”
塞巴斯蒂安·沃兹尼

1

如果您直接在组件模板中使用数组,则可以使用不纯管道。(此示例适用于不需要深度检查的简单数组)

@Pipe({
  name: 'arrayChangeDetector',
  pure: false
})
export class ArrayChangeDetectorPipe implements PipeTransform {
  private differ: IterableDiffer<any>;

  constructor(iDiff: IterableDiffers) {
    this.differ = iDiff.find([]).create();
  }

  transform(value: any[]): any[] {
    if (this.differ.diff(value)) {
      return [...value];
    }
    return value;
  }
}
<cmp [items]="arrayInput | arrayChangeDetector"></cmp>

对于那些我们当中仍然遇到阵列问题的旅行者,这里是对问题的再现以及几种可能的解决方案。

https://stackblitz.com/edit/array-value-changes-not-detected-ang-8

解决方案包括:

  • NgDoCheck
  • 使用管道
  • 使用不可变的JS NPM github

1

这对我有用:

@Component({
  selector: 'my-component',
  templateUrl: './my-component.component.html',
  styleUrls: ['./my-component.component.scss']
})
export class MyComponent implements DoCheck {

  @Input() changeArray: MyClassArray[]= [];
  private differ: IterableDiffers;

  constructor(private differs: IterableDiffers) {
    this.differ = differs;
  }

  ngDoCheck() {
    const changes = this.differ.find(this.insertedTasks);
    if (changes) {
      this.myMethodAfterChange();
  }
}

我为此加油,比其他任何方式对我都有帮助。不确定为什么标记有减号!
r3plica

2
据我所知,这不是changes总是设置的解决方案-这意味着您可以在this.myMethodAfterChange();inside内进行操作ngDoCheck。这会起作用,但会导致性能问题。
NoRyb
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.