Шаблон реактивных форм углового материала - как сделать компонент для этого шаблона с помощью 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 будет выглядеть примерно так:

my-form-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;
}

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

custom-form-field-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>

custom-form-field-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]]
    });
  }
}
person G. Tranter    schedule 22.01.2019
comment
не совсем - обратите внимание на разницу между вашим my-form-field.html и моим примером - в моем примере я запекал <mat-error>formGroup.get(formControl).hasError('server-validation')</mat-error> - я хочу избежать повторного написания этого. - person Joey Gough; 06.02.2019
comment
Я привел пример, а не окончательное решение, показывающее, как сделать содержимое ошибки многоразовым. Я ожидал, что вы сможете выяснить, что вам нужно, чтобы превратить пример в решение, соответствующее вашим потребностям. Замените текст Ошибка проверки сервера на {{formGroup.get('name').getError('serverValidation')}}, если вы имели в виду getError, а не hasError, которое вы написали. - person G. Tranter; 06.02.2019
comment
Да, я получил неопределенную форму formGroup - извините, что это заняло так много времени - был занят - я пытаюсь реализовать с помощью atm controlValueAccessor. - person Joey Gough; 06.02.2019
comment
Мне все еще не удалось это сделать, но я нашел это очень интересным - stackoverflow.com/a/44732530/8896573 - person Joey Gough; 07.02.2019
comment
Есть лучшие способы сделать это. Вставьте объект NgControl в конструктор constructor(@Optional() @Self() public ngControl: NgControl) и назначьте пользовательский компонент, реализующий ControlValueAccessor, в качестве средства доступа к элементу управления if (this.ngControl) this.ngControl.valueAccessor = this;. Это позволит вам использовать директиву формы любого типа — реактивную или шаблонную, ngModel, formControl или formControlName. Если вы попытаетесь обернуть MatInput внутри вашего компонента и хотите, чтобы это было фактическим полем, вам придется пройти через гораздо больше обручей. - person G. Tranter; 07.02.2019
comment
хороший - спасибо - это круто - также задокументировано здесь material.angular.io/guide/ - person Joey Gough; 08.02.2019