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

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

传值情况:

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

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

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

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

举例说明:

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

javascript 复制代码
// 父组件
@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 对象可以获取到新值和旧值进行比较,从而进行相应的处理。

javascript 复制代码
// 父组件
@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 获取子组件的引用,并传递值给子组件:

javascript 复制代码
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);
  }
}

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

javascript 复制代码
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 成为一个静态查询,只在 组件初始化时 进行一次查询。这意味着子组件只会获取到父组件初始时的值,并且不会随着父组件值的变化而自动更新。

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

javascript 复制代码
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); // 手动更新子组件中的变量
  }
}

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

javascript 复制代码
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 方法来更新子组件中的变量。

相关推荐
灵犀学长几秒前
解锁HTML5页面生命周期API:前端开发的新视角
前端·html·html5
江号软件分享9 分钟前
轻松解决Office版本冲突问题:卸载是关键
前端
致博软件F2BPM16 分钟前
Element Plus和Ant Design Vue深度对比分析与选型指南
前端·javascript·vue.js
慧一居士1 小时前
flex 布局完整功能介绍和示例演示
前端
DoraBigHead1 小时前
小哆啦解题记——两数失踪事件
前端·算法·面试
一斤代码7 小时前
vue3 下载图片(标签内容可转图)
前端·javascript·vue
中微子7 小时前
React Router 源码深度剖析解决面试中的深层次问题
前端·react.js
光影少年7 小时前
从前端转go开发的学习路线
前端·学习·golang
中微子7 小时前
React Router 面试指南:从基础到实战
前端·react.js·前端框架
3Katrina7 小时前
深入理解 useLayoutEffect:解决 UI "闪烁"问题的利器
前端·javascript·面试