【问题标题】:Angular2: *ngFor does not update when array is updatedAngular2:更新数组时*ngFor不更新
【发布时间】:2017-07-21 14:03:16
【问题描述】:

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

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

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

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

【问题讨论】:

    标签: angular angular2-changedetection


    【解决方案1】:

    您还可以在 *ngFor 表达式中使用 trackBy 选项,为数组中的每个项目提供唯一 ID。这确实使您 100% 负责更改检测,因此每次数组中的项目更改时更新此(唯一)属性。只有在数组中的任何项目被赋予不同的trackBy 属性时,Angular 才会重新渲染列表:

    *ngFor="let item of (itemList$ | async); trackBy: trackItem"
    

    或:

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

    地点:

    trackItem 是组件中的公共方法:

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

    【讨论】:

    • 这是最好的答案
    【解决方案2】:

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

    将 ChangeDetectorRef 添加到您的导入中:

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

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

    constructor(
        private changeDetection: ChangeDetectorRef
      ) { }
    

    然后在更新数组后的下一行:

    this.changeDetection.detectChanges();
    

    【讨论】:

    • 如果 trackby 没有检测到数组的变化,请使用这个示例并享受它。
    • 这几行代码救了我!谢谢
    • 只有有效的代码。
    【解决方案3】:

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

    this.arr = cloneDeep(this.arr);

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

    【讨论】:

    • 这个不需要 lo-dash。 this.arr = this.arr.map(el => Object.assign({}, el)this.arr = [...this.arr] 它是一个基元数组。
    【解决方案4】:
    1. 检查您的组件是否配置了 changeDetection:cHangeDetectionStrategy.OnPush,如果您要这样做,那么在更新数组后您必须调用 changeDetectorRef.markForCheck()
    2. 您还可以在此函数中实现 onChange 生命周期挂钩并更改数组的值。

    【讨论】:

      【解决方案5】:

      我通过在@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 策略,这意味着自动更改检测被停用,直到通过将策略设置为 Default (CheckAlways) 重新激活。仍然可以显式调用更改检测。

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

      来源Docs

      【讨论】:

      • 奇怪的是,您必须指定要使用 default 值。默认情况下,您的配置中是否有任何地方可以将 changeDetectionStrategy 定义为 onPush ?
      • 是的,在其他一些组件中,在将 Angular 与 Firestore 一起使用时,在某些情况下我必须指定 onPush changeDetectionStrategy,尤其是在将本地数组与硬编码到服务文件中的数据一起使用时。
      【解决方案6】:

      尝试通过以下方式创建深层副本

      this.arr = Object.assign({}, NEW_VALUE);
      

      【讨论】:

      • 不是深拷贝,是浅拷贝。而且这段代码适用于对象,而不适用于数组,不是吗?
      【解决方案7】:

      我可以使用 ngZone 更新组件

      export class comp {
        arr: type[] = []
        
        constructor (
          private zone: NgZone
        ) {}
      
        updateArr(data: type) {
           this.arr.push(this.zone.run(() => data))
        }
      
      }
      

      【讨论】:

        【解决方案8】:

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

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

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

        【讨论】:

          【解决方案9】:

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

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

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

          【讨论】:

            【解决方案10】:

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

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

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

            示例

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

            // 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
            
            

            【讨论】:

              【解决方案11】:

              每当我需要处理需要更改检测的 *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)'
              

              【讨论】:

                猜你喜欢
                • 2019-07-11
                • 2016-09-24
                • 2016-03-31
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 2019-01-26
                • 2016-12-18
                • 2016-08-08
                相关资源
                最近更新 更多