Angular 组件怎么检测Input为引用类型的变化 ?

Angular 组件 和 外界通信最基本的方式有2种,外界出入组件,使用Input 属性,组件传出数据,使用Output属性。Input 的属性的变化可以在ngOnChanges 钩子函数中检测到,但是如果Input属性的类型不是原始类型,如数组,数组的单个成员变化,如使用Array.splice/push 等方法改变了输入属性,改怎么监听呢?

监听 Array成员的改变

一般数组成员的改变是由一些常用的方法push splice pop, [index] = 操作引起 ,怎么监听这些操作导致数组的改变呢? 利用Angular 内置变检类IterableDiffers 来实现

实现步骤:

  1. 构造函数中注入 IterableDiffers 实例
// 产品Differ
 private productsDiffer: IterableDiffer<any>;
constructor(private iterableDiffers:IterableDiffers) {}
  1. 创建一个IterableDiffer实例
ngOnInit(): void {
    this.productsDiffer = this.iterableDiffers.find(this.products).create();
 }
  1. 检测变化
ngDoCheck(): void {
    // IterableDiffer.diff
     const diff =  this.productsDiffer.diff(this.products);
     if(diff){
       console.log('products is changed in ngDoCheck');
     }
 }

trackByFn

如果想监听第三级,也就是数组对象的属性的变化,以上步骤还需要调整,create 方法转入trackByFn

// 新增
private trackByFn(index: number, item: any) {
    return item.name;
 }

//修改
ngOnInit(): void {
    this.productsDiffer = this.iterableDiffers.find(this.products)
                                                                                            .create(this.trackByFn);
  }

测试实例代码

app-shop


import { Component, OnInit } from '@angular/core';
import { products } from './data';

@Component({
  selector: 'app-shop',
  // templateUrl: './shop.component.html',
  template: `
     <app-product-list [products]="products"></app-product-list>
     <p>
        <button (click)="test1()">[index]=</button>
        <button (click)="test2()">push</button>
        <button (click)="test()">reset</button>
        <button (click)="test3()">change object attribute</button>
      </p>
  `,
})
export class ShopComponent implements OnInit {
  public products  =  [];
  constructor() { }

  ngOnInit(): void {
    this.products = [...products];
  }
  // 使用数组索改变数组
  test1():void {
    this.products[1]= {
      name:'realme',
      description:'a new brand phone',
      price: 499
    }
  }
  // 使用push 方法
  test2():void {
    this.products.push({
      name:'xiaomi',
      description:'you will love it',
      price: 549
    });
  }
  // 重新赋值
  test(): void {
    this.products =[...products];
  }
  // 更深一级,改变对象的某一个属性
  test3(): void {
     this.products[0].name = 'iphone pro'
  }

}

app-product-list

import {
  Component,
  Input,
  IterableDiffer,
  IterableDiffers,
  OnInit,
  SimpleChanges
} from '@angular/core';

// import { products } from '../data';

@Component({
  selector: 'app-product-list',
  // templateUrl: './product-list.component.html',
  template:`
        <h2>Products</h2>
        <div *ngFor="let product of products;index as productId">
          <h3>
            <a [title]="product.name + ' details'"  >
              {{ product.name }}
            </a>
          </h3>
          <p *ngIf="product.description">
            Description: {{ product.description }}
          </p>
          <button (click)="share()">
            Share
          </button>
        </div>
      `,

})
export class ProductListComponent implements OnInit {
  @Input() products: Array<any> = [];
  // 产品Differ
  private productsDiffer: IterableDiffer<any>;
  constructor(private iterableDiffers: IterableDiffers) {}
  ngOnInit(): void {
    this.productsDiffer = this.iterableDiffers.find(this.products)
                                                                                            .create(this.trackByFn);
  }
  //
  ngDoCheck(): void {
    // IterableDiffer.diff
    const diff = this.productsDiffer.diff(this.products);
    if (diff) {
      console.log('products is changed in ngDoCheck');
    }
  }
  ngOnChanges(changes: SimpleChanges): void {
    if (changes['products']) {
      console.log('products changed in ngOnChanges');
    }
  }

  private trackByFn(index: number, item: any) {
    return item.name;
  }
  share(): void {
    window.alert('The product has been shared!');
  }

}

today
906 声望41 粉丝