Angular2:更新数组时* ngFor不会更新

Angular2: *ngFor does not update when array is updated

我有一个对象数组(我们称它为 arr)。在 (change) 方法中我的组件输入之一中,我修改了这些对象的属性之一,但在视图 (*ngFor) 中没有任何变化。我读到 Angular2 变化检测不检查数组或对象的内容,所以我尝试了这些:

this.arr = this.arr.slice();

this.arr = [...this.arr];

但是视图没有改变,它仍然显示旧属性。在带有 console.log()(change) 方法中,我得到了正确的数组。很奇怪,但这一个有效:this.arr = []; 我也尝试了 NgZonemarkForCheck()

尝试创建深拷贝

this.arr = Object.assign({}, NEW_VALUE);
  1. 检查您的组件是否配置了 changeDetection:cHangeDetectionStrategy.OnPush,如果您要这样做,那么在更新数组后您必须调用 changeDetectorRef.markForCheck()
  2. 你也可以在这个函数中实现onChange生命周期钩子,改变数组的值。

您还可以在 *ngFor 表达式中使用 trackBy 选项,为数组中的每个项目提供唯一 ID。这确实让你 100% 负责变化检测,所以每次数组中的项目发生变化时更新这个(唯一的)属性。 Angular 将仅在数组中的任何项目被赋予不同的 trackBy 属性:

时才重新呈现列表
*ngFor="let item of (itemList$ | async); trackBy: trackItem"

或:

*ngFor="let item of itemList; trackBy: trackItem"

其中:

trackItem 是组件中的一个 public 方法:

public trackItem (index: number, item: Item) {
  return item.trackId;
}

我通过在 @component 上添加一个 changDetection 指令解决了这个错误,如下所示

    @Component({
      selector: 'app-page',
      templateUrl: './page.component.html',
      styleUrls: ['./page.component.scss'],
      changeDetection: ChangeDetectionStrategy.Default
    })

您还需要导入它

import { Component, OnInit, ChangeDetectionStrategy } from '@angular/core';

有两种策略onPush和Default

onPush 使用 CheckOnce 策略,这意味着自动更改检测被停用,直到通过将策略设置为默认 (CheckAlways) 重新激活。仍然可以显式调用更改检测。

而默认使用 CheckAlways 策略,其中更改检测是自动的,直到明确停用。

来源Docs

既然你提到你已经尝试过 markForCheck(),你应该试试 detectChanges(这对我有用,而 markForCheck 没用)。对于那些需要步骤的人:

将 ChangeDetectorRef 添加到您的导入:

import { Component, OnInit, ChangeDetectorRef } from '@angular/core';

将 ChangeDetectorRef 添加到您的构造函数中:

constructor(
    private changeDetection: ChangeDetectorRef
  ) { }

更新数组后的下一行:

this.changeDetection.detectChanges();

万一其他人遇到我的情况,请确保正在更新的组件与正在渲染的组件是同一副本。

我使用 @ViewChild(MyComponent, { static: true }) private test: MyComponent 通过 ngfor 将数据传递给组件。 (在我的情况下,最终锁定到另一个我不知道的副本)

我可以通过将属性 #mycomp 添加到我在 html 中的组件标签并将上面的内容更改为 @ViewChild('mycomp', { static: true }) private test: MyComponent

来修复它

游戏迟到了,但使用 lodash 创建深度克隆对我有用

this.arr = cloneDeep(this.arr);

https://lodash.com/docs/4.17.15#cloneDeep

不是完美的解决方案,但有效:

const temp = this.arr;
this.arr = [];
temp.push(obj); // or any other changes
this.arr = temp;

我真的不喜欢导入新东西,因为会增加包的大小,在某些情况下会导致内存问题,所以我这样处理。

如果您重新分配数组,更改检测将不会注意到更改。因此,不要像 this.arr = ... 这样重新分配数组,而是直接更改数组。这可能看起来像这样:

this.arr.splice(0); // empty the array, without reassigning it
this.arr.push(...); // push new items

这样您就不需要手动调用更改检测。

例子

假设有一个 table 的列表,可以过滤(用户可以即搜索)。所以我将有一个包含所有可能项目的 list,以及一个在 UI 中使用并显示给用户的 filteredList

// in ngOnInit I load the full list
this.list = this.myService.getList();
this.filteredList = [...this.list] // Since I will change the filtered list directly instead of reassigning it, I need to make a copy to not affect the original full list.

// then in a filter function, i.e. filterList()
this.filteredList.splice(0);
this.filteredList.push([...this.list.filter(/* ... */)]); // Again I need to push copies

每当我需要处理需要更改检测的 *ngFor 时,我更喜欢使用 behaviorSubject 和异步管道。 (我知道这是一个更长的解决方案。使用行为主题可以很容易地进行变化检测)

array$ = new BehaviorSubject([]);//Declare your array

当您需要更新数组时

array$.next(newArray)://Pass in new array data

如果您想读取 .ts 文件中的数组值,请使用此

array$.getValue()

在您的 .HTML 文件中

*ngFor='let obj of (array$ | async)'

我能够使用 ngZone

更新组件
export class comp {
  arr: type[] = []
  
  constructor (
    private zone: NgZone
  ) {}

  updateArr(data: type) {
     this.arr.push(this.zone.run(() => data))
  }

}