Angular 13中如何将子组件Input模型类的每次变更推送至父组件
Angular 13 子组件@Input模型变更实时推送父组件实现
你现有代码存在核心错误:直接对普通模型对象mdlInData调用subscribe()是无效的,只有Observable实例才有订阅方法,这段代码运行会直接抛错。另外如果直接修改@Input传入对象的属性值,虽然子组件模板的双向绑定能同步显示变更,但不符合Angular单向数据流规范,在OnPush变更检测模式下父组件无法感知变更。
下面是两种可直接落地的实现方案:
方案1:原生双向绑定语法糖(最推荐,符合Angular设计规范)
Angular支持[()]双向绑定语法,只要输出事件的命名满足输入属性名 + Change的规则,就可以实现自动同步,不需要额外写冗余的事件接收逻辑。
子组件实现代码
import { Component, Input, Output, EventEmitter, OnInit } from '@angular/core'; @Component({ selector: 'app-child', template: ` <h1>子组件</h1> <!-- 模板内表单控件绑定示例 --> <input type="text" [(ngModel)]="mdlInData.Name" (ngModelChange)="syncLatestData()"> ` }) export class ChildComponent implements OnInit { @Input() mdlInData: any; // 输出名必须和输入名对应,后缀为Change才能触发双向绑定语法糖 @Output() mdlInDataChange = new EventEmitter<any>(); ngOnInit(): void { // 初始化修改属性 this.mdlInData.id = 1; this.mdlInData.Name = "Test"; this.mdlInData.phone = ['7867878', '768689678']; // 修改完成后同步给父组件 this.syncLatestData(); } // 所有对mdlInData的修改操作完成后,调用这个方法推送最新值 syncLatestData() { // 做深拷贝切断父子组件的直接引用关联,避免意外的互相修改 // 兼容低版本环境可替换为 JSON.parse(JSON.stringify(this.mdlInData)) const latestValue = structuredClone(this.mdlInData); this.mdlInDataChange.emit(latestValue); } }
父组件调用方式
直接用双向绑定语法即可,父组件的parentModel会自动同步所有子组件推送的变更:
<app-child [(mdlInData)]="parentModel"></app-child>
方案2:BehaviorSubject托管状态(适合属性多、修改点分散的复杂场景)
如果模型属性多、修改逻辑分散,不想每次修改后手动调用同步方法,可以用RxJS的BehaviorSubject托管模型状态,自动监听变更推送。
子组件实现代码
import { Component, Input, Output, EventEmitter, OnInit, OnDestroy } from '@angular/core'; import { BehaviorSubject, Subject, distinctUntilChanged, takeUntil } from 'rxjs'; @Component({ selector: 'app-child', template: `<h1>子组件</h1>` }) export class ChildComponent implements OnInit, OnDestroy { @Input() mdlInData: any; @Output() mdlOutData = new EventEmitter<any>(); // 用来托管模型状态的流 private mdlStream$ = new BehaviorSubject<any>(null); // 用来取消订阅防止内存泄漏 private destroy$ = new Subject<void>(); ngOnInit(): void { // 初始化把传入的模型值推到流里 this.mdlStream$.next(this.mdlInData); // 订阅流的变更,自动做去重后推送给父组件 this.mdlStream$.asObservable() .pipe( distinctUntilChanged((prev, curr) => JSON.stringify(prev) === JSON.stringify(curr)), takeUntil(this.destroy$) ) .subscribe(latest => this.mdlOutData.emit(latest)); // 所有模型修改都通过updateModel方法统一更新 this.updateModel({ ...this.mdlInData, id: 1, Name: "Test", phone: ['7867878', '768689678'] }); } // 统一的模型更新入口 updateModel(newValue: any) { this.mdlInData = newValue; this.mdlStream$.next(newValue); } ngOnDestroy(): void { this.destroy$.next(); this.destroy$.complete(); } }
父组件调用方式
手动绑定输入和输出事件即可:
<app-child [mdlInData]="parentModel" (mdlOutData)="parentModel = $event"></app-child>
注意:不推荐直接在子组件里修改@Input传入的对象属性后依赖引用共享让父组件拿到值,这种方式会让数据流变得不可追踪,一旦父组件开启OnPush变更检测就会出现值不同步的问题。
内容的提问来源于stack exchange,提问作者C47_C0D3R
相关产品推荐
相关产品推荐

