【问题标题】:Dynamically create a <mat-error> component and have it be projected into the parent <mat-form-field> component properly动态创建 <mat-error> 组件并将其正确投影到父 <mat-form-field> 组件中
【发布时间】:2019-10-22 17:28:57
【问题描述】:

使用ComponentFactoryResolver 动态创建组件很简单,但是当我尝试对&lt;mat-form-field&gt; 中的Angular Material &lt;mat-error&gt; 组件执行此操作时,它似乎不会将创建的组件投影到父组件中.我一直在使用 Netanel Basal 的 example 和 Bootstrap 组件,它非常适合生成表单控制错误,但由于 Angular Material 中使用 &lt;mat-error&gt; 的内容投影,我似乎无法让它适用于 Angular Material v8.2.1.

是否可以在&lt;mat-form-field&gt; 中动态创建&lt;mat-error&gt; 并使其内容项目正确?

【问题讨论】:

  • 有一些我们可以查看的代码..?
  • 为什么需要这样做?一个mat-form-field只需要一个mat-error——动态改变错误内容很简单。
  • 发布一个 stackblitz 会很有帮助,您正在使用的 @angular/material 版本......MatFormField 已经发生了一些变化
  • @G.Tranter 我们不想将mat-error 添加到可以按需动态生成的每个表单字段中。我们绝对可以有一个mat-error,然后应用一个方法{{ getError('formGroup.formControl') }},并查看错误消息,但我们会将该方法添加到每个组件,它只会增加模板膨胀。
  • @mt@mtpultz 如果您有这么多表单字段并且计算错误的规则在它们之间是一致的,那么您可以根据处理错误处理的mat-form-field 创建自己的组件。跨度>

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


【解决方案1】:

我之前尝试过这个 - MatFormField 通过 ContentChildren 接收 MatErrors。因为它是内容投影,所以动态添加或删除像MatError 这样的元素是行不通的——内容模板的编译和处理方式与其他模板不同。

您在评论中提到您正在尝试跨模板处理一致的错误消息...为什么不添加一个 directive &lt;mat-error&gt; 元素来控制基于以下的错误消息父控件的表单验证状态?

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

@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 的存在,就像它应该做的那样,但是您以本地化、干净的方式控制错误元素的内容

上面的stackblitz:https://stackblitz.com/edit/angular-sjkfft

您必须深入研究 Material 组件的私有成员,这是有问题的做法,并且可能会因库更新而中断,但它确实有效。

我实际上有一个针对这种类型的错误消息的存储库,它是为更旧版本的@angular/material 编写的,但我也能够获取Validator 本身以获得更好的错误消息,并且是将该验证器的整个自定义验证器/错误列表注入到模块中:https://github.com/joh04667/material-error-messages

【讨论】:

  • 那么,如果我在mat-error 上使用指令,我是否能够访问FormControl 来确定错误?我想远离像&lt;mat-error&gt;{{ getErrorMessage('formControlName') }}&lt;/mat-error&gt; 这样的事情,并且很高兴每个组件都有&lt;mat-error appErrorMessage&gt;&lt;/mat-error&gt;,因为它可以避免将getError 方法添加到每个组件中。我有点认为这可能是不可能的,因为内容投影,尤其是在尝试过它之后,但希望我错了,但该指令是一个不错的选择。
  • 是的!在上面的代码中,注入器抓取 MatFormField。在那个 MatFormField 上,FormControl 是 MatFormField._control.ngControl。想想看,你也许可以直接注入FormControl 来获取引用,但我目前无法测试。
  • 好的,我检查了,你不能注入 FormControl 或 NgControl。但是,我确实启动并运行了这个 stackblitz:stackblitz.com/edit/angular-sjkfft。查看我链接的 repo,了解如何将自定义 Validators 和丰富的数据放入错误中!
【解决方案2】:

这只是另一种替代解决方案,受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 ??

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2019-09-19
    • 2020-02-23
    • 2020-10-26
    • 2018-04-16
    • 1970-01-01
    • 2019-05-29
    • 1970-01-01
    相关资源
    最近更新 更多