Рективные формы в Angular

Jul 16, 2018

Подписываемся на изменения значения и ошибки

Следим и получаем данные при помощи реактивного подхода:

this.nameCtrl = new FormControl('Vasya');
this.nameCtrl.valueChanges.subscribe((data) => {
    console.log('data: ', data);
})

Отслеживаем ошибки при помощи реактивного подхода:

this.nameCtrl.statusChanges.subscribe((status) => {
    console.log('status: ', status);
})

Валидаторы для реактивных форм

Синхронный валидатор:

Когда нам требуется передать валидатору какое-либо значение, нужно сделать функцию-обертку, которая будет давать возможность по замыканию сохранять это значение.

ngOnInit() {
    this.nameCtrl = new FormControl('Vasya', [myValidator(11)], [myAsyncValidator]);
}

function myValidator(number) { // это просто функция-обертка, чтобы по замыканию сохранить значение, так как наш валидатор получает значение

    return function(formCtrl: FormControl) {
        if (formCtrl.value.length < number) {
            return { myValidator: { message: 'error on length' } };
        }
        return null;
    }

}

Асинхронный валидатор:

Асинхронный валидатор в отличие от обычного валидатора должен возвращать Observable.

function myAsyncValidator(formCtrl: FormControl): Observable<null|any> {
    if (formCtrl.value.length < number) {
        return Observable.of({ myValidator: { message: 'error on length' } });
    }
    return Observable.of(null);
}

FormArray

FormArray - позволяет работать со списками.
Данные FormArray сериализуются как массив (в отличие от FormGroup, где данные сериализуются как объект). Это становится полезным, когда вы не знаете сколько контролов могут быть представлены в группе, например, для динамических форм.

FormGroup vs. FormArray на stackoverflow

<section>
    <p>Any special requests?</p>
    <ul formArrayName="specialRequests">
        <li *ngFor="let item of orderForm.controls.specialRequests.controls; let i = index">
            <input type="text" formControlName="{{i}}">
            <button type="button" title="Remove Request" (click)="onRemoveSpecialRequest(i)">Remove</button>
        </li>
    </ul>
    <button type="button" (click)="onAddSpecialRequest()">
        Add a Request
    </button>
</section>
constructor () {
    this.orderForm = new FormGroup({
        firstName: new FormControl('Nancy', Validators.minLength(2)),
        lastName: new FormControl('Drew'),
        specialRequests: new FormArray([
          new FormControl(null)
        ])
    });
}

onSubmitForm () {
    console.log(this.orderForm.value);
}

onAddSpecialRequest () {
    this.orderForm.controls
        .specialRequests.push(new FormControl(null));
}

onRemoveSpecialRequest (index) {
    this.orderForm.controls['specialRequests'].removeAt(index);
}

FormBuilder

FormBuilder - упрощает работу по созданию сложных форм. FormBuilder - это сервис, который необходимо inject.

    public addCategoryForm: FormGroup;
    public name = new FormControl('', [Validators.required]);
    public position = new FormControl('', [Validators.required, Validators.pattern('^[0-9]+$')]);

    constructor(private fb: FormBuilder) {
    }

    ngOnInit() {
        let self = this;
        self.addCategoryForm = self.fb.group({
            name: self.name,
            position: self.position,
            users: self.fb.array([['Vasya'], ['Petya']])
        });

Кастомные контролы

Чтобы превратить наш компонент в FormControl нужно:

  1. Реализовать (implements) интерфейс ControlValueAccessor
  2. В провайдере доопределить ...
  3. Определим какие действия нам требуется выполнять в шаблоне (например, реагировать на клик)
@Component({
    selector: 'app-my-control',
    providers: [{
        provide: NG_VALUE_ACCESSOR,
        useExisting: forwardRef(() => MyControlComponent), // как провайдер используем этот же класс;
                // forwardRef заворачивает наш класс внутрь функции и отдает ее
                // это нужно так как мы не можем указать наш класс сразу (Angular будет ругаться, так как класс задан после его вызова)
        multi: true  // ????
    }]
})
export class MyControlComponent implements ControlValueAccessor {
    // 4 метода от ControlValueAccessor, которые необходимо реализовать
    // (эти методы объяснят контролу, как реагировать на изменения):
    writeValue(obj: any): void;

    registerOnChange(fn: any): void;

    registerOnTouched(fn: any): void;

    setDisabledState?(isDisabled: boolean): void;
}
Добавить комментарий