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

pab*_*ado 57 javascript typescript angular

我有一个父组件,它使用ajax请求检索对象数组.

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

更改对象中的某个字段时会发生此问题:子组件未通知这些更改.只有在手动将数组重新分配给其变量时才会触发更改.

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

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

Sei*_*vic 90

OnChanges Lifecycle Hook仅在输入属性的实例更改时触发.

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

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

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

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

有关更多信息,请访问此帖子:检测Angular2中数组内对象的更改

  • 记得像这样初始化 iterableDiffer ```iterableDiffer:IterableDiffer<YourSingleItemType>;``` (5认同)
  • 哎呀。我刚刚和我的验光师预约了新眼镜。 (3认同)
  • `diff` 方法已重命名为 `find`:https://angular.io/api/core/IterableDiffers (2认同)
  • @Sparafusile您的链接指向`IterableDiffers`,而不是`IterableDiffer`接口。请检查:https://angular.io/api/core/IterableDiffer (2认同)
  • ngDoCheck在资源上很昂贵,因为我将它与find方法一起使用,但是@SeidMehmedovic的方式是性能最好的方法。竖起大拇指 (2认同)

Woj*_*ski 12

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

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

this.yourArray = [].concat(this.yourArray);
Run Code Online (Sandbox Code Playgroud)

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

  • @WojtekMajerski:“你只创建一个指向与前一个相同的内存区域的新变量” - 它不能指向相同的区域(这就是为什么它们首先被认为是不同的), `Array.concat` 总是创建一个新实例,因此对初始数组元素的所有引用都会被复制(浅复制)。一旦你执行了`var x = [].concat(y)`,你就可以分别推送到`x`和`y`,它们是独立的。如果您之后不使用 y ,则 if 将会被 GC 收集。 (4认同)

小智 10

阅读以下文章,不要错过mutable与immutable对象.

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

我在我的一个项目中使用redux store来避免这种问题.

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


Gün*_*uer 6

您可以使用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);
  }
}
Run Code Online (Sandbox Code Playgroud)


Sta*_*ver 6

这对我来说是工作:

@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();
  }
}
Run Code Online (Sandbox Code Playgroud)

  • 据我所知,这不是一个解决方案,因为“changes”总是被设置的 - 这意味着你可以在“ngDoCheck”中执行“this.myMethodAfterChange();”。这可以工作,但会产生性能问题。 (2认同)