Шаблон реактивных форм углового материала - как создать компонент для этого шаблона с помощью ControlValueAccessor

Работа с угловыми материалами mat-form-field и реактивные формы. В проекте у меня есть повторяющийся шаблон, который выглядит следующим образом

// ts
this.formGroup = this.formBuilder.group({
    name: ['', ServerValidation]
})

<!-- html -->
<div [formGroup]="formGroup">
  <mat-form-field>
    <input
      matInput
      formControlName="name"
      [placeholder]="'Name'"
      name="name"
     />
    <mat-error
     *ngIf="
       formGroup
       .get('name')
       .hasError('serverValidation')
       "
      >
      {{
       formGroup
       .get("name")
       .getError("serverValidation")
      }}
     </mat-error>
   </mat-form-field>
</div>

Это высокий уровень - принимая, что я могу получать ошибки проверки с сервера - как я могу повторить этот шаблон http-шаблона в компоненте? У меня есть догадка, что я должен использовать ControlValueAccessor - но не знаю, как это сделать.

Реализация, которую я представляю, может выглядеть примерно так

<!-- html -->
<div [formGroup]="formGroup">
  <serverValidatedInput formControlName="'name'">
    <mat-error>error message for client side validation</mat-error>
  </serverValidatedInput>
</div>

По сути, я хочу использовать этот пользовательский компонент как обычный материал ввода (более или менее), за исключением того, что он поставляется с ошибкой проверки сервера по умолчанию. Кто-нибудь может дать мне какое-то направление здесь - спасибо.:)

1 ответ

Есть два способа сделать это - простой и сложный. Сложный путь заключается в реализации ControlValueAccessor и это дает вам больше гибкости в использовании компонента. Самый простой способ - просто передать вещи через ваш компонент фактическим элементам формы внутри. Если вам не нужна гибкость в использовании этого компонента, выберите легкий путь.

Во-первых, вам нужно избавиться от идеи использования mat-error вне mat-form-control, Это просто не сработает, и вам не нужно, чтобы это работало. Оставьте его внутри поля формы и предоставьте для него содержимое. Наряду с этим примените свою логику ошибок к содержанию mat-errorне к mat-error сам. И помните, что вам не нужна логика для отображения mat-error - поле формы автоматически позаботится об этом, когда элемент управления формы имеет ошибку. Вам нужна только логика, чтобы определить содержание ошибки.

Простая обертка для mat-form-field будет выглядеть примерно так:

моя форма-field.html

<mat-form-field>
  <input matInput type="text" [placeholder]="placeholder" [formControl]="myFormControl" required>
  <mat-error>
    <ng-content></ng-content>
  </mat-error>
</mat-form-field>

мой форм-field.ts

import {Component, Input} from '@angular/core';
import {FormControl} from '@angular/forms';

@Component({
  selector: 'my-form-field',
  templateUrl: 'my-form-field.html'
})
export class MyFormField {
  @Input() myFormControl: FormControl;
  @Input() placeholder: string;
}

использование

пользовательских форм-поля example.html

<form [formGroup]="formGroup">
  <my-form-field placeholder="Name" [myFormControl]="formGroup.get('name')">
    <ng-container *ngIf="formGroup.get('name').hasError('required')">
     This field is required
    </ng-container>
    <ng-container *ngIf="formGroup.get('name').hasError('serverValidation')">
      Server validation failed
    </ng-container>
  </my-form-field>
</form>

пользовательских форм-поля-example.ts

import {Component} from '@angular/core';
import {FormBuilder, FormGroup, Validators} from '@angular/forms';
import {ServerValidation} from '...';

@Component({
  selector: 'custom-form-field-example',
  templateUrl: 'custom-form-field-example.html'
})
export class CustomFormFieldExample {
  formGroup: FormGroup;

  constructor(formBuilder: FormBuilder) {
    this.formGroup = formBuilder.group({
      name: ['', [Validators.required, ServerValidation]]
    });
  }
}