You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Angular父子组件表单联动:子表单变更同步至父组件并双向绑定

实现Angular父子组件表单双向同步与嵌套整合

需求概述

我有一个包含两个子组件的父组件,需要子组件表单内容变更时实时同步到父组件,同时将嵌套表单作为Input传入子组件,实现双向联动。

初始代码结构

父组件初始表单:

profileForm = new FormGroup({
  firstName: new FormControl(''),
  lastName: new FormControl(''),
  address: new FormGroup({}),
});

子组件1现有代码:

@Output() onFormEmit = new EventEmitter<any>();
form = new FormGroup({
  child1Key1: new FormControl(''),
  child1Key2: new FormControl(''),
  child1Key3: new FormControl(''),
});
ngOnInit() {
  this.onFormEmit.emit(this.form);
}

子组件2现有代码:

@Output() onFormEmit = new EventEmitter<any>();
form = new FormGroup({
  child2Key4: new FormControl(''),
  child2Key5: new FormControl(''),
});
ngOnInit() {
  this.onFormEmit.emit(this.form);
}

期望最终父组件表单结构

profileForm = new FormGroup({
  firstName: new FormControl(''),
  lastName: new FormControl(''),
  address: new FormGroup({
    child1Key1: new FormControl(''),
    child1Key2: new FormControl(''),
    child1Key3: new FormControl(''),
    child2Key4: new FormControl(''),
    child2Key5: new FormControl(''),
  }),
});

解决方案

方案一:直接共享父组件嵌套FormGroup(简单高效)

不再让子组件创建独立表单,而是直接接收父组件的address FormGroup,将子组件控件挂载到该组下,天然实现双向同步。

子组件1修改

import { Component, Input, OnInit } from '@angular/core';
import { FormGroup, FormControl } from '@angular/forms';

@Component({
  selector: 'app-child1',
  templateUrl: './child1.component.html',
})
export class Child1Component implements OnInit {
  @Input() parentFormGroup!: FormGroup;

  ngOnInit() {
    // 向父组件的address表单添加子组件控件
    this.parentFormGroup.addControl('child1Key1', new FormControl(''));
    this.parentFormGroup.addControl('child1Key2', new FormControl(''));
    this.parentFormGroup.addControl('child1Key3', new FormControl(''));
  }
}

子组件1模板:

<div [formGroup]="parentFormGroup">
  <input formControlName="child1Key1" placeholder="子组件1字段1">
  <input formControlName="child1Key2" placeholder="子组件1字段2">
  <input formControlName="child1Key3" placeholder="子组件1字段3">
</div>

子组件2修改

import { Component, Input, OnInit } from '@angular/core';
import { FormGroup, FormControl } from '@angular/forms';

@Component({
  selector: 'app-child2',
  templateUrl: './child2.component.html',
})
export class Child2Component implements OnInit {
  @Input() parentFormGroup!: FormGroup;

  ngOnInit() {
    this.parentFormGroup.addControl('child2Key4', new FormControl(''));
    this.parentFormGroup.addControl('child2Key5', new FormControl(''));
  }
}

子组件2模板:

<div [formGroup]="parentFormGroup">
  <input formControlName="child2Key4" placeholder="子组件2字段4">
  <input formControlName="child2Key5" placeholder="子组件2字段5">
</div>

父组件模板修改

将address FormGroup传递给子组件:

<form [formGroup]="profileForm">
  <input formControlName="firstName" placeholder="名">
  <input formControlName="lastName" placeholder="姓">
  
  <div formGroupName="address">
    <app-child1 [parentFormGroup]="profileForm.get('address') as FormGroup"></app-child1>
    <app-child2 [parentFormGroup]="profileForm.get('address') as FormGroup"></app-child2>
  </div>
</form>

方案二:实现ControlValueAccessor(规范自定义表单控件)

如果需要子组件保持独立表单逻辑,可通过实现ControlValueAccessor接口,将子组件封装为自定义表单控件,无缝集成到父组件表单中。

子组件1示例

import { Component, forwardRef } from '@angular/core';
import { ControlValueAccessor, NG_VALUE_ACCESSOR, FormGroup, FormControl } from '@angular/forms';

@Component({
  selector: 'app-child1',
  templateUrl: './child1.component.html',
  providers: [
    {
      provide: NG_VALUE_ACCESSOR,
      useExisting: forwardRef(() => Child1Component),
      multi: true
    }
  ]
})
export class Child1Component implements ControlValueAccessor {
  childForm = new FormGroup({
    child1Key1: new FormControl(''),
    child1Key2: new FormControl(''),
    child1Key3: new FormControl('')
  });

  onChange: any = () => {};
  onTouched: any = () => {};

  writeValue(value: any): void {
    if (value) {
      this.childForm.setValue(value, { emitEvent: false });
    }
  }

  registerOnChange(fn: any): void {
    this.onChange = fn;
    this.childForm.valueChanges.subscribe(fn);
  }

  registerOnTouched(fn: any): void {
    this.onTouched = fn;
  }

  setDisabledState?(isDisabled: boolean): void {
    isDisabled ? this.childForm.disable() : this.childForm.enable();
  }
}

子组件1模板:

<div [formGroup]="childForm">
  <input formControlName="child1Key1" placeholder="子组件1字段1">
  <input formControlName="child1Key2" placeholder="子组件1字段2">
  <input formControlName="child1Key3" placeholder="子组件1字段3">
</div>

父组件配置

profileForm = new FormGroup({
  firstName: new FormControl(''),
  lastName: new FormControl(''),
  address: new FormGroup({
    child1: new FormGroup({
      child1Key1: new FormControl(''),
      child1Key2: new FormControl(''),
      child1Key3: new FormControl('')
    }),
    child2: new FormGroup({
      child2Key4: new FormControl(''),
      child2Key5: new FormControl('')
    })
  }),
});

父组件模板:

<form [formGroup]="profileForm">
  <input formControlName="firstName" placeholder="名">
  <input formControlName="lastName" placeholder="姓">
  
  <div formGroupName="address">
    <app-child1 formControlName="child1"></app-child1>
    <app-child2 formControlName="child2"></app-child2>
  </div>
</form>

内容的提问来源于stack exchange,提问作者paulotarcio

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.08 23:30:48