首页 > 解决方案 > 从父组件动态设置子表单验证器

问题描述

我一直在 Angular 8 中创建可重用的子表单,并且使用 为子表单FormGroupDirective获取父表单的引用FormGroup很容易完成,但我很难弄清楚如何在子表单上动态设置验证器从父母。

使用ngAfterViewInit我可以看到子表单的表单控件已添加到父表单,但我似乎无法直接从父表单更新默认子表单验证器。

// PARENT COMPONENT
public ngAfterViewInit() {
  // Form controls have been applied to the form group
  console.log('ngAfterViewInit', this.form.value);

  // BUT the validators of profile form aren't being set?
  this.form.get('profile').get('description').setValidators([Validators.required]);
}

我创建了我的示例代码的StackBlitz。除配置文件描述外的所有字段均为必填字段并具有默认值。因此,无需对验证器进行任何更改,表单将在提交时通过验证并在控制台中输出VALID 。ngAfterViewInit我将描述字段验证器设置为必需,这应该防止表单被提交并在控制台中输出INVALID,但它仍然提交并输出VALID

是否可以动态设置子表单的验证器而不通过@Input绑定传递它们?

标签: angularangular-reactive-formsangular-formsangular-validation

解决方案


显然,您只需要使用updateValueAndValidity已更改的表单控件即可。

this.form.get('profile').get('description').updateValueAndValidity();

所以它最终看起来像这样:

public ngAfterViewInit() {
  // Form controls will now exist
  console.log('ngAfterViewInit', this.form.value);

  // BUT the validators of the profile form should not allow submission if set to required, and they do?

  this.form.get('profile').get('description').setValidators([Validators.required]);
  this.form.get('profile').get('description').updateValueAndValidity();
}

当你这样做时,你会在控制台中看到一个错误,上面写着:

ERROR
Error: ExpressionChangedAfterItHasBeenCheckedError: 
Expression has changed after it was checked. 
Previous value: 'ng-valid: true'. Current value: 'ng-valid: false'.

这可以通过使用ChangeDetectorRef和调用来克服this.changeDetector.detectChanges(),或者通过将父组件的更改检测策略更新为ChagneDetectionStrategy.OnPush.

constructor(private changeDetector: ChangeDetectorRef) { }

// Removed for brevity

public ngAfterViewInit() {
  const descControl = this.form.get('profile').get('description');
  descControl.setValidators([Validators.required]);
  descControl.updateValueAndValidity();

  this.changeDetector.detectChanges();
}
@Component({
  selector: 'app-page',
  templateUrl: './page.component.html',
  styleUrls: ['./page.component.scss'],
  changeDetection: ChangeDetectionStrategy.OnPush
})
export class PageComponent implements OnInit, AfterContentInit, AfterViewInit {
  // Removed for brevity

  public ngAfterViewInit() {
    const descControl = this.form.get('profile').get('description');
    descControl.setValidators([Validators.required]);
    descControl.updateValueAndValidity();
  }
}

推荐阅读