如何使用模板表单从 Angular 2 中的父组件触发子组件验证器

How to trigger child component validators from parent component in Angular 2 using template forms

我在 Angular 中使用模板表单进行条件验证时遇到问题。我创建了一个自定义 EmailInputComponent:

<div class="form-group" provide-parent-form>
  <label for="email">Email<span *ngIf="required">*</span></label>
  <input id="email"
         class="form-control"
         name="email"
         type="email"
         [(ngModel)]="emailAddress"
         #email="ngModel"
         maxlength="255"
         validateEmail
         [required]="required ? '' : null"/>
  <error [model]="email" [referencedValue]="emailAddress"></error>
</div>

托管在父 MyFormComponent 中:

<form #form="ngForm" name="form" (ngSubmit)="onSubmit($event)">
  <fieldset>
    <email [(emailAddress)]="model.email" [required]="emailRequired()"></email> 
    <!-- select component here -->
  </fieldset>    
  <button type="submit" [disabled]="!form.form.valid">Send</button>
</form>

该表单还包含一个 SelectComponent,用户可以在其中选择他们喜欢的联系方式。如果用户 select "email",则必须输入电子邮件。

如您所见,请注意父项中存在一些逻辑 emailRequired 根据当前 selected 首选联系方式动态计算电子邮件输入是否强制的功能。 每当这个 selected 值发生变化时,我都需要以某种方式触发电子邮件输入验证器。我怎样才能做到这一点?

使用 @ViewChild 我设法从 MyFormComponent 获取了 EmailInputComponent。但我现在不知道如何进行...

我认为你不需要在前面指定attr来引用required 属性。

尝试简单的,

 [required]="required ? '' : null"

如果仍然无效,可能是因为“@Input”required 属性 未在 EmailInputComponent 中更新。因此,@naeramarth7 建议在 EmailInputComponent 的 ngOnChanges 挂钩中查找它并更新。

ngOnChanges(changes: SimpleChange) {
 for (let prop in changes) {
  if(prop === 'required') { 
   this.required = changes[prop].currentValue;
  }
 }
}

确保在使用它时将 implements OnChanges 添加到 EmailInputComponent 声明中,并在 email-input.component.ts

同时我找到了解决办法。我的子组件现在引用相应的 FormGroup。

<email [(emailAddress)]="model.email" 
       [formGroup]="form.form" 
       [required]="emailRequired()">
</email>

通过 FormGroup,您可以到达子组件实际的 FormControl 并在验证器标志的 setter 内调用 updateValueAndValidityset在我的例子中需要):

import {Component, EventEmitter, Input, Output} from '@angular/core';
import {FormGroup} from '@angular/forms';

@Component({
  selector: 'email',
  templateUrl: './email.component.html',
  styleUrls: ['./email.component.css']
})
export class EmailInputComponent {

  private _emailAddress: string;

  public _required = true;

  @Input()
  private formGroup: FormGroup;

  @Output()
  emailAddressChange = new EventEmitter();

  constructor() { }

  @Input()
  get emailAddress(): string {
    return this._emailAddress;
  }

  set emailAddress(value: string) {
    this._emailAddress = value;
    this.emailAddressChange.emit(this._emailAddress);
  }

  @Input()
  get required(): boolean {
    return this._required;
  }

  set required(value: boolean) {
    this._required = value;
    // this is where the magic happens
    const refEmailControl = this.formGroup.controls.email;
    if (refEmailControl) {
      refEmailControl.updateValueAndValidity(); // updates the validity state      
      refEmailControl.markAsTouched(); // necessary in my case to make UI render error message
    }
  }
}