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

Angular通过内容投影添加条件表单字段 解决找不到控件错误

问题描述

我正在尝试通过内容投影添加条件表单字段。
目前投影的输入字段可正常根据同级复选框状态切换显隐:复选框处于“勾选”状态时条件字段显示,处于“未勾选”状态时输入字段消失。
但当我将复选框切换为勾选状态时,会收到如下错误:

ERROR Error: Cannot find control with name: 'detail'

我希望投影的detail字段归属于子组件内部的_formGroup,而非父组件的FormGroup,该如何实现?
该错误表明formControlName="detail"无法被子组件的_formGroup识别,该如何修复?
以下为相关代码:

父组件

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

@Component({
  selector: 'ch-parent-comp',
  templateUrl: './parent-comp.component.html',
  styleUrls: ['./parent-comp.component.scss']
})
export class ParentComponent implements OnInit {

  formGroup: FormGroup;
  constructor() { 

    this.formGroup = new FormGroup({
        //detail: new FormControl('') --> 加这行能解决问题,但我不想把这个字段放在父组件,我要它归属于子组件
    });
  }

  ngOnInit(): void {
  }

}

父组件HTML模板

<form>
    <ch-check-box-group [parentFormGroup]="formGroup" controlName="new_property" controlLabel="New Property" [hasDetail]="true" [detailRequired]="true">
        <!-- 投影的输入字段 -->
        <ng-template contentHandle>
            <mat-form-field>
                <mat-label>Purchase Date</mat-label>
                <input type="text" matInput name="detail" formControlName="detail" id="">
            </mat-form-field>
        </ng-template>
    </ch-check-box-group>
</form>

子组件(ch-check-box-group)

export interface CheckboxGroupForm {
  value: FormControl<boolean>,
  detail?: FormControl<any>
}

@Component({
  selector: 'ch-check-box-group',
  templateUrl: './check-box-group.component.html',
  styleUrls: ['./check-box-group.component.scss']
})
export class CheckBoxGroupComponent implements OnInit, OnDestroy {

  private _ks: Subject<void> = new Subject<void>();
  _formGroup: FormGroup
  _showDetailInput: boolean = false;

  @Input() controlLabel!: string;
  @Input() controlName!: string;
  @Input() parentFormGroup!: FormGroup;
  @Input() hasDetail: boolean = false;
  @Input() detailRequired: boolean = false;

  @ContentChild(ContentHandleDirective) content!: ContentHandleDirective;

  constructor() { 
    this._formGroup = new FormGroup<CheckboxGroupForm>({
      value : new FormControl(false, {nonNullable: true}),
    });
  }

  ngOnInit(): void {
    if(this.hasDetail){
      this._formGroup.addControl('detail', new FormControl(''));
    }
    this.parentFormGroup.addControl(this.controlName, this._formGroup);
    this._formGroup.setParent(this.parentFormGroup);

    this._formGroup.valueChanges
      .pipe(takeUntil(this._ks))
      .subscribe((change) => {
        // 切换详情输入框显隐
        if(this.hasDetail && this._showDetailInput != change.value){
          this._showDetailInput = change.value;
        }

        // 按需切换详情输入框校验规则
        if(this.hasDetail && this.detailRequired && change.value){
          setTimeout(() => {
            this._formGroup.get('detail')?.addValidators(Validators.required);
          });
        } else {
          this._formGroup.get('detail')?.clearValidators();
          if(this._showDetailInput != change.value){
            this._formGroup.get('detail')?.updateValueAndValidity();
          }
        }
      });
  }

  ngOnDestroy(){
    this._ks.next();
    this._ks.complete();
  }
}

子组件模板

<div class="row">
    <form class="col-12" [formGroup]="_formGroup">
        <mat-checkbox formControlName="value" i18n>{{ controlLabel }}</mat-checkbox>
        <button mat-icon-button color="primary">
            <mat-icon>help</mat-icon>
        </button>
        <div class="conditional-input-box-container" *ngIf="_showDetailInput">
            <ng-container [ngTemplateOutlet]="content.templateRef"></ng-container>
        </div>
    </form>
</div>

问题原因

报错核心是Angular模板注入上下文的继承规则:写在父组件中的ng-template,即使最终通过ngTemplateOutlet在子组件内渲染,它的表单控制注入上下文仍归属父组件,不会自动获取子组件模板中[formGroup]="_formGroup"提供的FormGroupDirective实例。因此formControlName="detail"会默认去父组件根formGroup中查找detail控件,自然匹配失败。

修复方案

渲染模板时把子组件的FormGroupDirective作为模板上下文传入,让投影内容能正确获取子组件的表单组实例,共三步修改:

  • 第一步:在子组件类中注入自身的FormGroupDirective实例
// 引入对应依赖
import { FormGroupDirective } from '@angular/forms';

export class CheckBoxGroupComponent implements OnInit, OnDestroy {
  // 原有属性声明不变
  constructor(
    // 注入当前子组件表单对应的FormGroupDirective
    private _formGroupDirective: FormGroupDirective
  ) { 
    this._formGroup = new FormGroup<CheckboxGroupForm>({
      value : new FormControl(false, {nonNullable: true}),
    });
  }
  // 其余原有逻辑不变
}
  • 第二步:修改子组件模板的ngTemplateOutlet配置,将注入的表单指令作为上下文传递
<div class="row">
    <form class="col-12" [formGroup]="_formGroup">
        <mat-checkbox formControlName="value" i18n>{{ controlLabel }}</mat-checkbox>
        <button mat-icon-button color="primary">
            <mat-icon>help</mat-icon>
        </button>
        <div class="conditional-input-box-container" *ngIf="_showDetailInput">
            <ng-container 
                [ngTemplateOutlet]="content.templateRef"
                [ngTemplateOutletContext]="{ formGroupDirective: _formGroupDirective }">
            </ng-container>
        </div>
    </form>
</div>
  • 第三步:修改父组件中的投影模板,显式绑定传入的子组件表单组
<ng-template contentHandle let-formGroupDirective="formGroupDirective">
    <ng-container [formGroup]="formGroupDirective.form">
        <mat-form-field>
            <mat-label>Purchase Date</mat-label>
            <input type="text" matInput name="detail" formControlName="detail" id="">
        </mat-form-field>
    </ng-container>
</ng-template>

修改完成后,投影的detail控件会正确注册到子组件内部的_formGroup上,既不会再报找不到控件的错误,也不需要在父组件FormGroup中声明detail字段。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.28 07:09:25