动态创建<mat-error>组件,并将其正确投影到父<mat-form-field>组件中

时间:2019-10-22 17:28:57

标签: angular angular-material angular-reactive-forms

使用ComponentFactoryResolver动态创建组件很简单,但是当我尝试对其中的Angular Material <mat-error>组件执行此操作时,似乎并没有将创建的组件投影到父对象中<mat-form-field>。我一直在使用带有Bootstrap组件的Netanel Basal的example,它非常适合生成表单控制错误,但是由于在Angular Material中与<mat-error>一起使用的内容投射,我似乎无法理解适用于Angular Material v8.2.1。

是否可以在<mat-error>中动态创建一个<mat-form-field>并使其内容正确地投影?

2 个答案:

答案 0 :(得分:1)

我之前尝试过-MatFormField通过MatError拾取ContentChildren。由于是内容投影,因此无法动态添加或删除诸如MatError之类的元素-内容模板的编译和处理方式与其他模板不同。

您在评论中提到,您正在尝试处理模板之间的一致错误消息...为什么不向directive元素添加<mat-error> 基于父控件的表单验证状态的错误消息?

但是,如果要控制模板,则不能使用指令。但是,您可以创建一个组件,并像指令一样使用

@Component({
  selector: '[matErrorMessages]',
  template: '{{ error }}'
})
export class MatErrorMessagesDirective implements AfterViewInit {

  public error = '';
  private inputRef: MatFormFieldControl<MatInput>;

  constructor(private _inj: Injector) { }

  public ngAfterViewInit() {
    // grab reference to MatFormField directive, where form control is accessible.
    let container = this._inj.get(MatFormField);
    this.inputRef = container._control;

    // sub to the control's status stream
    this.inputRef.ngControl.statusChanges.subscribe(this.updateErrors);
  }


  private updateErrors = (state: 'VALID' | 'INVALID') => {
    if (state === 'INVALID') {
      // active errors on the FormControl
      let controlErrors = this.inputRef.ngControl.errors;

      // just grab one error
      const firstError = Object.keys(controlErrors)[0];

      if(firstError === 'required')
        this.error = 'This field is required.';

      if(firstError === 'minlength')
        this.error = 'This field should be longer.';

      if(firstError === 'error from my own custom validator')
        this.error = 'You get the point.';
      // ..... 
    }
  }

然后进入模板。...

<mat-error matErrorMessages></mat-error>

这样,您可以像预期的那样,让MatFormField控制MatError的存在,但可以在本地化的地方控制错误元素的 content ,干净的方式。

上面的

stackblitz:https://stackblitz.com/edit/angular-sjkfft

您必须挖掘Material组件的私有成员,这是一个有问题的做法,并且可能会随着库更新的进行而中断,但这是可行的。

我实际上有一个针对此类错误消息的存储库,该存储库是为@ angular / material的较旧版本编写的,但是我也能够掌握Validator本身,以实现更好的错误消息传递,并将该验证器的自定义验证器/错误的整个列表注入模块:https://github.com/joh04667/material-error-messages

答案 1 :(得分:0)

这是另一种替代解决方案,受ngx-valdemort的启发,创建了一个用于处理控件验证错误消息的组件,只需要在formGroup主体中成为子组件并传递表单控件的名称即可,不仅限于角形材料分量。

@Component({
  selector: 'error-summary',
  templateUrl: './error-summary.component.html',
  styleUrls: ['./error-summary.component.css']
})
export class ErrorSummaryComponent  {

  @Input() control:string;

  @Input() visible:any;

  constructor(private controlContainer: ControlContainer) { }

  get form():FormGroup {
    return this.controlContainer.control  as FormGroup;
  }

  get formControl() :AbstractControl{
    return this.form.get(this.control) as AbstractControl;
  }

  get isNotValid() {
    return this.formControl.invalid && (this.formControl.touched || this.formControl.dirty)
  }

}

模板

<ng-container *ngIf="isNotValid"  >
  <ng-container *ngIf="formControl.hasError('required')"> this is required </ng-container>
  <ng-container *ngIf="formControl.hasError('email')">this not valid email format</ng-container>
  <ng-container *ngIf="formControl.hasError('maxlength')">max ...</ng-container>
  <ng-container *ngIf="formControl.hasError('minlength')">min ...</ng-container>
</ng-container>

并且可以像这样使用

<form [formGroup]="form">

  <mat-form-field>
        <input matInput placeholder="Enter your email" formControlName="email">
  </mat-form-field>
    <error-summary control="email" ></error-summary>

  ...

</form>

demo ??