angular——子组件如何接收父组件的动态传值

开发过程中,父组件给子组件传值的情况很常见,今天我们就来聊聊父组件给子组件传值可能会发生哪些意外,什么情况下子组件无法接收到父组件最新的传值;

传值情况:

  1. 基本数据类型:父组件给子组件传递 基本数据类型,子组件使用变量接收传值;当传值发生变化,子组件接收的值也会跟着变化;

  2. 引用数据类型:当父组件传递给子组件的 数据对象(对象或引用类型) 发生变化时,子组件接收到的值不一定会跟着变化;

以下是一些情况下子组件中的变量不会自动更新的场景:

  1. 输入属性为对象或引用类型: 如果父组件传递给子组件的输入属性是一个对象或引用类型(如数组);
    并且在父组件中修改了该对象的属性或对其进行了重新赋值,子组件中的变量本身不会发生变化。
    原因:变更检测机制只会检测到对象或引用本身的变化,而不会深度观察对象内部的属性或内容的变化。
    在这种情况下,需要手动处理以确保子组件中的变量更新,可以使用 OnChanges 生命周期钩子、set方法接收、订阅 Input 属性的变化来实现。
  2. 使用 @ViewChild 或服务来共享数据: 如果父组件通过 @ViewChild 或服务(例如共享服务)来传递数据给子组件,子组件中的变量通常不会自动更新。这是因为 @ViewChild 或服务提供的数据是一个单独的实例,与父组件中的数据不直接关联。在这种情况下,需要手动更新子组件中的变量,例如使用订阅机制或事件触发等方式。

举例说明:

1. 子组件使用set方法接收传值

// 父组件
@Component({
    
    
  selector: 'app-parent',
  template: `
    <app-child [data]="parentData"></app-child>
    <button (click)="changeData()">Change Data</button>
  `
})
export class ParentComponent {
    
    
  parentData = {
    
     value: 'Initial data' };

  changeData() {
    
    
    this.parentData.value = 'Updated data';
  }
}

// 子组件
@Component({
    
    
  selector: 'app-child',
  template: `<p>{
     
     { childData }}</p>`
})
export class ChildComponent {
    
    
  private _data: any;

  @Input()
  set data(value: any) {
    
    
    this._data = value;
    // 在这里可以对数据进行进一步处理
  }

  get childData(): any {
    
    
    return this._data;
  }
}

在上述示例中,父组件通过 parentData 对象将数据传递给子组件的 data 输入属性。子组件使用 set 方法接收该数据,并将其存储在私有变量 _data 中。

当点击 “Change Data” 按钮时,父组件的 changeData() 方法将修改 parentData.value 的值为 ‘Updated data’。此时,子组件中的 set 方法会被触发,并接收到新的数据对象 { value: ‘Updated data’ }。子组件可以在 set 方法内部对数据进行相应的处理以更新子组件的状态。

set 方法的触发是由 Angular 的变更检测机制 自动处理的,无需手动触发该方法。

set 方法触发时机

  1. 初始化时触发: 当父组件传递的数据被子组件初始化时,set 方法会在子组件创建和渲染过程中首次触发。这是设置初始值的时机。

  2. 属性变化时触发: 如果父组件的数据发生了变化(例如由于用户交互或异步请求等),Angular 的变更检测机制会检测到属性的变化,并触发子组件中的 set 方法,从而更新子组件的相应属性。

get 方法触发时机

get 方法的触发是由子组件主动读取属性值时自动触发的,而不是由 Angular 的变更检测机制触发的。因此,它并不会在每次属性变化时都触发。只有当子组件需要获取属性值时,get 方法才会被调用。
例如,在子组件的模板中使用插值表达式、绑定属性、或者在子组件的 TypeScript 代码中直接访问该属性时,get 方法会被调用。

2. 使用 ngOnChanges 生命周期钩子

在该钩子中监听父组件传入的输入属性变化,并在变化时手动更新子组件内部的变量。通过 SimpleChange 对象可以获取到新值和旧值进行比较,从而进行相应的处理。

// 父组件
@Component({
    
    
  selector: 'app-parent',
  template: `
    <app-child [data]="parentData"></app-child>
    <button (click)="changeData()">Change Data</button>
  `
})
export class ParentComponent {
    
    
  parentData = {
    
     value: 'Initial data' };

  changeData() {
    
    
    this.parentData.value = 'Updated data';
  }
}

// 子组件
@Component({
    
    
  selector: 'app-child',
  template: `<p>{
     
     { childData }}</p>`
})
export class ChildComponent implements OnChanges {
    
    
  @Input() data: any;
  childData: any;

  ngOnChanges(changes: SimpleChanges) {
    
    
    if (changes.data && changes.data.currentValue) {
    
    
      this.childData = changes.data.currentValue;
    }
  }
}

3. ViewChild 使用static: false 传递数据

如果父组件通过 @ViewChild 或服务(例如共享服务)来传递数据给子组件,并且使用 static: true,那么当父组件的值发生变化时,子组件通常不会自动更新。

相反,使用 static: false,那么当父组件的值发生变化时,子组件也会相应地更新。

原理:
当使用 @ViewChild 来获取对子组件的引用时,如果将 static 设置为 false,它会使得 @ViewChild
成为一个查询,会在每次变更检测时重新查询子组件。这意味着如果父组件的值发生变化,子组件的引用会重新查询并得到更新。

在父组件中,通过 @ViewChild 获取子组件的引用,并传递值给子组件:

import {
    
     Component, ViewChild } from '@angular/core';
import {
    
     ChildComponent } from './child.component';

@Component({
    
    
  selector: 'app-parent',
  template: `
    <app-child></app-child>
    <button (click)="updateData()">Update Parent Data</button>
  `
})
export class ParentComponent {
    
    
  @ViewChild(ChildComponent, {
    
     static: false })
  childComponent: ChildComponent;

  parentData = {
    
     name: 'John', age: 25 };

  updateData(): void {
    
    
    this.parentData = {
    
     name: 'Jane', age: 30 };
    this.childComponent.data = this.parentData;
    // 或者调用子组件的方法进行更新:this.childComponent.updateData(this.parentData);
  }
}

在子组件中,定义一个属性来接收父组件的值,并在模板中显示该数据:

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

@Component({
    
    
  selector: 'app-child',
  template: `
    <div>Name: {
     
     { data.name }}</div>
    <div>Age: {
     
     { data.age }}</div>
  `
})
export class ChildComponent {
    
    
  @Input()
  data: any;
  
}

在上述示例中,当父组件的 updateData 方法被调用时,它会更新 parentData 的值,并将新值传递给子组件的 data 属性。由于 @ViewChild 使用了 static: false,子组件的引用会重新查询,在变更检测期间子组件会获取到新的值并进行更新。

总结来说,如果你使用 @ViewChild 并设置了 static: false,当父组件的值发生变化时,子组件也会随之更新。这使得父组件能够直接影响到子组件的状态和行为。

4. ViewChild 使用static: true 传递数据

使用 @ViewChild 来获取对子组件的引用时,如果将 static 设置为 true,它会使得 @ViewChild 成为一个静态查询,只在 组件初始化时 进行一次查询。这意味着子组件只会获取到父组件初始时的值,并且不会随着父组件值的变化而自动更新。

在这种情况下,需要手动更新子组件中的变量以反映父组件的变化。可以使用订阅机制、事件触发等方式,在父组件的值发生变化时通知子组件进行更新。

import {
    
     Component, ViewChild } from '@angular/core';
import {
    
     ChildComponent } from './child.component';

@Component({
    
    
  selector: 'app-parent',
  template: `
    <app-child></app-child>
    <button (click)="updateData()">Update Parent Data</button>
  `
})
export class ParentComponent {
    
    
  @ViewChild(ChildComponent, {
    
     static: true })
  childComponent: ChildComponent;

  parentData = {
    
     name: 'John', age: 25 };

  updateData(): void {
    
    
    this.parentData = {
    
     name: 'Jane', age: 30 };
    this.childComponent.updateData(this.parentData); // 手动更新子组件中的变量
  }
}

在子组件中,定义一个方法来接收父组件的值,并在模板中显示该数据:

import {
    
     Component } from '@angular/core';

@Component({
    
    
  selector: 'app-child',
  template: `
    <div>Name: {
     
     { data.name }}</div>
    <div>Age: {
     
     { data.age }}</div>
  `
})
export class ChildComponent {
    
    
  data: any;

  updateData(newData: any): void {
    
    
    this.data = newData;
  }
}

在上述示例中,当父组件的 updateData 方法被调用时,它会更新 parentData 的值,并手动调用子组件的 updateData 方法来更新子组件中的变量。

猜你喜欢

转载自blog.csdn.net/weixin_45678402/article/details/132149090