Пользовательский Form Control на Angular

Apr 14, 2019

Наш компонент будет компонентом, который можно интегрировать в любую форму Angular с поддержкой всех шести свойств, которые формы добавляют к элементам управления: pristine, dirty, valid, invalid, touched, untouched.

Реализуем компонент выбора штата/страны в раскрывающемся списке:

@Component({
selector: 'state-selector',
template: `
<select [name]="name" [(ngModel)]="value">
    <option *ngFor="let state of stateList" [value]="state.abbreviation">
        {{state.name}}
    </option>
</select>`
})
export class StateSelectorComponent {

    @Input() name: string;

    @Input('value') val: string;
}

Выше приведена основа нашего компонента.

Далее чтобы превратить наш кастомный компонент в пользовательский form control нужно на нем реализовать интерфейс ControlValueAccessor:

export class StateSelectorComponent implements ControlValueAccessor

Вот контракт, который мы должны реализовать от этого интерфейса:

export interface ControlValueAccessor {
    /**
     * Writes a new value to the element.
     */
    writeValue(obj: any): void;
    /**
     * Registers a callback function that should be called when
     * the control's value changes in the UI.
     */
    registerOnChange(fn: any): void;
    /**
     * Registers a callback function that should be called when
     * the control receives a blur event.
     */
    registerOnTouched(fn: any): void;
    /**
     * This function is called by the forms API when
     * the control status changes to or from "DISABLED".
     */
    setDisabledState?(isDisabled: boolean): void;
}

Эти 4 метода описывает то, что API формы ожидает о нашего пользовательского элемента управления:

  • Быть в состоянии записать новое значение (writeValue)
  • Получать уведомления, когда изменилось значение (используя функцию переданную в registerOnChange)
  • Получать уведомления, когда получил 'touched' (используя функцию переданную в registerOnTouched)
  • Опционально наш пользовательский контрол может быть включен/выключен (setDisabledState)

Ниже приведена базовая реализация для нашего StateSelectorComponent:

export class StateSelectorComponent implements ControlValueAccessor  {

  @Input() name: string;
  @Input('value') val: string;

  stateList: State[] = states;
  // Both onChange and onTouched are functions
  onChange: any = () => { };
  onTouched: any = () => { };

  get value() {
    return this.val;
  }

  set value(val) {
    this.val = val;
    this.onChange(val);
    this.onTouched();
  }

  // We implement this method to keep a reference to the onChange
  // callback function passed by the forms API
  registerOnChange(fn) {
    this.onChange = fn;
  }

  // We implement this method to keep a reference to the onTouched
  //callback function passed by the forms API
  registerOnTouched(fn) {
    this.onTouched = fn;
  }

  // This is a basic setter that the forms API is going to use
  writeValue(value) {
    if (value) {
      this.value = value;
    }
  }
}

Важно отметить, что мы должны сказать Angular об нашем новом кастомном элементе управления формы. Это происходут путем добавления его в список всех NG_VALUE_ACCESSOR в нашем списке providers:

@Component({
  selector: 'state-selector',
  providers: [
    {
      provide: NG_VALUE_ACCESSOR,
      useExisting: forwardRef(() => StateSelectorComponent),
      multi: true
    }
  ],
...

В результате любая форма, использующая наш селектор сможет использовать его как любой другой элемент формы, например, select или input.

Пример использования в шаблонной форме:

May 18, 2018

<form #form="ngForm" (ngSubmit)="logForm(form.value)">
    <state-selector name="state" ngModel #state="ngModel"></state-selector>
    <button type="submit">Submit</button>
</form>

<h2>Current selection:</h2>
<h3>{{state.value}}</h3>

Наш кастомный компонент автоматически поддерживает встроенные директивы такие как required и ngModel. Также он имеет свои свойства pristine, dirty, valid, invalid, touched, untouched, подобно любому другому контролу формы.

Пример на stackblitz

источник

Добавить комментарий