可重复使用的角反应形式(ControlValueAccessor)和材料步进器以及异步验证

时间:2020-01-09 07:42:49

标签: angular forms asynchronous stepper

我尝试将材料步进器(https://material.angular.io/components/stepper/overview)与嵌套可重用形式(https://coryrylan.com/blog/building-reusable-forms-in-angular)一起使用。在为嵌套表单构建异步验证器之前,它工作得很好:尽管表单有效,但stepper不会继续进行下一步。 我相信问题来自“ NG_VALIDATORS”,嵌套表单使用它来将验证器信息传输到父表单,正如文档所说:“ InjectionToken用于注册与AbstractControls一起使用的其他同步验证器。”我尝试改用NG_ASYNC_VALIDATORS,但我真的不知道它是如何工作的。我需要帮助。

代码如下:

import {Component, EventEmitter, forwardRef, OnDestroy, OnInit, Output} from '@angular/core';
import {
    ControlValueAccessor,
    NG_VALUE_ACCESSOR,
    FormBuilder,
    FormGroup,
    Validators,
    FormControl,
    NG_VALIDATORS,
    NG_ASYNC_VALIDATORS, ValidationErrors
} from '@angular/forms';
import {BehaviorSubject, Observable, of, Subscription} from 'rxjs';
import { ValidationService } from '../../validators/validation.service';
import { FirestoreService } from '../../../services/firestore/firestore.service';
import {map, startWith} from 'rxjs/operators';

export interface CustomerFormValues {
    firstName: string;
    lastName: string;
    email: string;
    phone: number;
    code: string;
}

@Component({
    selector: 'app-customer-form',
    templateUrl: './customer-form.component.html',
    styleUrls: ['./customer-form.component.scss'],
    providers: [
        {
            provide: NG_VALUE_ACCESSOR,
            useExisting: forwardRef(() => CustomerFormComponent),
            multi: true
        },
        {
            provide: NG_VALIDATORS,
            useExisting: forwardRef(() => CustomerFormComponent),
            multi: true,
        },
        {
            provide: NG_ASYNC_VALIDATORS,
            useExisting: forwardRef(() => CustomerFormComponent),
            multi: true,
        }
    ]
})
export class CustomerFormComponent implements ControlValueAccessor, OnDestroy, OnInit {

    form: FormGroup;
    subscriptions: Subscription[] = [];
    customers = [];
    customerFilteredOptions: Observable<Array<any>>;
    onCreateCustomer$  = new BehaviorSubject<boolean>(false);
    isReadOnly = false;
    isCodeReadOnly = false;
    get value(): CustomerFormValues {
        return this.form.getRawValue();
    }

    set value(value: CustomerFormValues) {
        this.form.setValue(value);
        this.onChange(value);
        this.onTouched();
    }

    get customerControl() {
        return this.form.controls.customer;
    }

    get firstNameControl() {
        return this.form.controls.firstName;
    }

    get lastNameControl() {
        return this.form.controls.lastName;
    }

    get emailControl() {
        return this.form.controls.email;
    }

    get phoneControl() {
        return this.form.controls.phone;
    }

    get codeControl() {
        return this.form.controls.code;
    }

    constructor(private formBuilder: FormBuilder,
                public validationService: ValidationService,
                private firestoreservice: FirestoreService) {
        this.firestoreservice.Customers$.subscribe(data => {
            this.customers = data;
        });

        this.form = this.formBuilder.group({
            firstName: ['', Validators.required],
            lastName: ['', Validators.required],
            email: ['', Validators.compose([Validators.email, Validators.required])],
            customer: [''],
            phone: ['', Validators.compose([Validators.maxLength(10), Validators.minLength(10), Validators.required])],
            code: ['', [Validators.required], [ValidationService.checkCustomerCodeExist(this.firestoreservice.Customers$,
                this.onCreateCustomer$)]]
        });

        this.subscriptions.push(
            this.form.valueChanges.subscribe(value => {
                this.onChange(value);
                this.onTouched();
            })
        );
    }

    ngOnInit(): void {
        this.customerFilteredOptions = this.customerControl.valueChanges
            .pipe(
                startWith(''),
                map(value => this._customerFilter(value))
            );
    }

    ngOnDestroy() {
        this.subscriptions.forEach(s => s.unsubscribe());
    }

    private _customerFilter(myCustomers): Array<any> {
        console.log('_customerFilter');
        if (myCustomers) {
            const filterValue = myCustomers.toLowerCase();

            return this.customers.filter(customer => customer.nom.toLowerCase().includes(filterValue)
                || customer.prenom.toLowerCase().includes(filterValue)
                || customer.email.toLowerCase().includes(filterValue)
                || customer.telephone.toLowerCase().includes(filterValue)
                || customer.code.toLowerCase().includes(filterValue)
            );
        }
    }

    onCustomerOptionSelected(mycode) {
        console.log('onCustomerOptionSelected');
        console.log(mycode);
        if (mycode !== '') {
            this.onCreateCustomer$.next(false);
            console.log('onCustomerOptionSelected');
            this.disableCustomerForm();
            this.onCreateCustomer$.next(false);
            const selectedCustomer = this.customers.filter(customer => customer.email === mycode);
            console.log(selectedCustomer[0].code);

            this.customerControl.setValue('');
            this.firstNameControl.setValue(selectedCustomer[0].nom);
            this.lastNameControl.setValue(selectedCustomer[0].prenom);
            this.emailControl.setValue(selectedCustomer[0].email);
            this.phoneControl.setValue(selectedCustomer[0].telephone);
            this.codeControl.setValue(selectedCustomer[0].code);
        }
    }

    disableCustomerForm() {
        this.isReadOnly = true;
        this.isCodeReadOnly = true;
    }

    enableCustomerForm() {
        this.isReadOnly = false;
        this.isCodeReadOnly = false;
    }

    onAddCustomer() {
        console.log('onAddCustomer');
        this.onCreateCustomer$.next(true);
        this.form.reset();
        this.enableCustomerForm();
    }

    onEditCustomer() {
        console.log('onEditCustomer');
        this.onCreateCustomer$.subscribe(val => {
            console.log('onEditCustomer val : ', val);
            if (!val) {
                this.enableCustomerForm();
                this.isCodeReadOnly = true;
            }
        })
            .unsubscribe();
    }

    onChange: any = () => {};
    onTouched: any = () => {};

    registerOnChange(fn) {
        this.onChange = fn;
    }

    writeValue(value) {
        if (value) {
            this.value = value;
        }
    }

    registerOnTouched(fn) {
        this.onTouched = fn;
    }

    validate(_: FormControl): Observable<ValidationErrors | null> {
        return of(this.form.valid ? null : { profile: { valid: false, }, });
    }
}

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

@Component({
  selector: 'app-stepper',
  templateUrl: './workshop-stepper.component.html',
  styleUrls: ['./workshop-stepper.component.scss']
})
export class WorkshopStepperComponent {
  isLinear = true;
  step1Form: FormGroup;
  step2Form: FormGroup;
  step3Form: FormGroup;
  step4Form: FormGroup;
  step5Form: FormGroup;
  step6Form: FormGroup;

  constructor(private formBuilder: FormBuilder) {
    this.step1Form = this.formBuilder.group({
      customer: []
    });

    this.step2Form = this.formBuilder.group({
      device: []
    });

  }

}
<div class="container-fluid content-wrapper">
  <h1 class="page-title">Prise en charge matériel en atelier</h1>

  <mat-horizontal-stepper [linear]="isLinear" #stepper style="background-color: #F9F9FA">
    <mat-step [stepControl]="step1Form">
      <form [formGroup]="step1Form">
        <ng-template matStepLabel>Client</ng-template>
        <app-customer-form formControlName="customer"></app-customer-form>
      </form>
    </mat-step>
    <mat-step [stepControl]="step2Form">
      <ng-template matStepLabel>Matériel</ng-template>
      <form [formGroup]="step2Form">
        <app-device-form formControlName="device" [customer]="step1Form.value"></app-device-form>
      </form>
    </mat-step>
    <mat-step [stepControl]="step3Form">
      <ng-template matStepLabel>Photos</ng-template>
    </mat-step>
    <mat-step [stepControl]="step4Form">
      <ng-template matStepLabel>Prestations</ng-template>
    </mat-step>
    <mat-step [stepControl]="step5Form">
      <ng-template matStepLabel>Validation</ng-template>
    </mat-step>
  </mat-horizontal-stepper>
</div>

1 个答案:

答案 0 :(得分:0)

对于那些被打扰的人来说,问题在于validate()函数没有等待“代码”输入的异步验证。因此,返回总是“无效”。我不得不修改validate()函数:

validate(_: FormControl) {
        return this.form.valid ? null : { profile: { valid: false, }, };
    }

收件人:

validate(_: FormControl) {
        if (this.codeControl.pending) {
            console.log('validate customer : pending');
            setTimeout(() => {
                this.validate(_);
            }, 100);
        } else {
            console.log('validate customer : ', this.form.valid);
            return this.form.valid ? null : { profile: { valid: false, }, };
        }
    }