【问题标题】:Angular 2: How to detect changes in an array? (@input property)Angular 2:如何检测数组中的变化? (@input 属性)
【发布时间】:2017-08-15 04:50:17
【问题描述】:

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

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

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

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

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

【问题讨论】:

  • @Adam 不要那么肤浅

标签: javascript angular typescript


【解决方案1】:

与其通过 concat 方法触发变更检测,不如使用 ES6 解构运算符更优雅:

this.yourArray[0].yourModifiedField = "whatever";
this.yourArray = [...this.yourArray];

【讨论】:

    【解决方案2】:

    OnChanges Lifecycle Hook 只会在输入属性的实例改变时触发。

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

    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。 (您可以与之前的检查同时进行)。

    访问此帖子了解更多信息:Detect changes in objects inside array in Angular2

    【讨论】:

    • diff 方法已重命名为 find: angular.io/api/core/IterableDiffers
    • @Sparafusile 您的链接指向IterableDiffers,而不是IterableDiffer 接口。请查收:angular.io/api/core/IterableDiffer
    • 糟糕。我刚刚和验光师预约了新眼镜。
    • 记得像这样初始化iterableDiffer iterableDiffer:IterableDiffer<YourSingleItemType>;
    • ngDoCheck 对资源来说是昂贵的,因为我已经将它与 find 方法一起使用,但 @SeidMehmedovic 所做的方式对于性能来说是最好的。竖起大拇指
    【解决方案3】:

    这对我有用:

    @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();
      }
    }
    

    【讨论】:

    • 我正在为此标记你,它对我的​​帮助比其他任何人都多。不知道为什么你被标记为减号!
    • 在我看来,这不是一个解决方案,因为changes 始终设置为 - 这意味着您可以在ngDoCheck 中执行this.myMethodAfterChange();。这会起作用,但会产生性能问题。
    【解决方案4】:

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

    @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

    【讨论】:

      【解决方案5】:

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

      【讨论】:

      • 你让我“使用 setInterval(myService.function, 1000) 让自己陷入陷阱”
      【解决方案6】:

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

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

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

      【讨论】:

      • 在我的情况下,这对我来说效果很好。
      • 从性能和内存消耗的角度来看,这如何?
      • @ErnestoAlfonso:这是一个零性能操作,因为您不克隆数组内容(元素)。您只需创建一个新变量,它指向与前一个相同的内存区域。
      • 你的回答拯救了我的一天
      • @WojtekMajerski:“你只创建了一个新变量,它指向与前一个相同的内存区域”——它不能指向同一个区域(这就是为什么它们首先被认为是不同的) , Array.concat 总是创建一个新实例,因此对初始数组元素的所有引用都被复制(浅复制)。一旦你做了var x = [].concat(y),你可以分别推送到xy,它们是独立的。如果您之后不使用y,则会被 GC 收集。
      【解决方案7】:

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

      【讨论】:

        【解决方案8】:

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

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

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

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

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2019-06-09
          • 2017-02-15
          • 2016-11-29
          • 2018-12-22
          • 2017-09-25
          • 2016-12-13
          • 2021-10-02
          相关资源
          最近更新 更多