markAllAsTouched 和 markAsTouched 都不影响子表单

Tud*_*las 6 angular angular-forms

背景资料

我有一个非常大的表单(可能有数百个字段),我决定将其分解为较小的子表单组件(出于明显的原因)。我使用控制值访问器实现了这一点。这就像一个魅力,直到需要在提交时显示字段上的错误。

子表单的结构如下:

<form [formGroup]="form">
  <app-subform [documentId]="documentId" formControlName="sub-form"></app-subform>

  <!-- Other form fields here ... -->
</form>
Run Code Online (Sandbox Code Playgroud)

为了简化我的工作,我创建了一个错误组件,它将一个控件作为输入,如下面的代码所示。下面的代码是子表单的一部分。

<div class="question">
  <label for="tableNumber">
    Figure/table number
    <app-required [type]="'conditional'"></app-required>
  </label>
  <input type="text" id="tableNumber" formControlName="figure"/>
  <app-error [control]="form.get('figure')"></app-error>
</div>
Run Code Online (Sandbox Code Playgroud)

我的错误 HTML 的内部看起来像这样:

<span class="errorWrapper" *ngIf="control.invalid && control.touched" (click)="clearError()" [innerHtml]="error"></span>
Run Code Online (Sandbox Code Playgroud)

示例代码

虽然我不能完全分享我的完整代码,但这里是问题的重现: https: //stackblitz.com/edit/encapsulated-sub-forms

问题

当我提交代码时,为了在每个输入中显示错误消息,我使用 标记所有输入为触摸this.form.markAllAsTouched();。虽然这对于不属于子表单组件的输入来说就像一个魅力,但子表单组件本身根本不会做出反应,保持不变。

我考虑的解决方案

  • 我的第一反应是 - 为什么不将每个控件单独标记为触摸?不起作用
  • 我为每个触发触摸标记的子表单组件实现一个输入怎么样?没有尝试,看起来更像是一种黑客而不是最佳实践解决方案
  • 将我的实现更改为使用ControlContainer. 看来是最有前途的

我尝试过的

我尝试更改要使用的实现ControlContainer,但这带来了一系列挑战(在单独的堆栈溢出问题中进行了描述)。

我的问题

所以基本上,我想知道的是,如何让我的子表单输入在提交时显示错误,遵循最佳实践并进行最少的重写(我的优先级按此顺序)?

Kur*_*ton 4

这里的问题是您的子表单组件正在声明一个全新的FormGroup实例。FormGroup它对父控件中的实例一无所知。

我在这里的方法是将根内的嵌套表单组FormGroup从父表单传递到子表单。

这将允许您的子表单更新父表单中的表单组,而无需双向绑定。表单组上触发的事件将通过子表单传播。

示例代码

父组件.ts

this.form = this.formBuilder.group({
  subGroup1: this.formBuilder.group({}),
  subGroup1: this.formBuilder.group({})
});
Run Code Online (Sandbox Code Playgroud)

父组件.html

<form [formGroup]="form">
  <app-sub-form-1 [form]="form.get('subGroup1')"></app-sub-form-1>
  <app-sub-form-2 [form]="form.get('subGroup2')"></app-sub-form-1>
</form>
Run Code Online (Sandbox Code Playgroud)

子表单1.component.ts

this.form.addControl('sub-group1-control1', new FormControl(''));
Run Code Online (Sandbox Code Playgroud)