【问题标题】:reusable angular reactive form (ControlValueAccessor) & material stepper & asynchronous validation可重用的角度反应形式(ControlValueAccessor)和材料步进器和异步验证
【发布时间】:2020-04-26 18:43:49
【问题描述】:

我尝试使用带有嵌套可重用表单 (https://coryrylan.com/blog/building-reusable-forms-in-angular) 的材料步进器 (https://material.angular.io/components/stepper/overview)。在我为嵌套表单构建异步验证器之前,它工作得很好:尽管表单有效,但步进器不会进入下一步。 我相信问题来自“NG_VALIDATORS”,嵌套表单使用它来将验证器信息传输到父表单,正如文档所说:“用于注册与 AbstractControls 一起使用的附加 同步 验证器的 InjectionToken。”我尝试改用 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>

【问题讨论】:

    标签: angular forms asynchronous stepper


    【解决方案1】:

    对于那些感兴趣的人来说,问题是 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, }, };
            }
        }

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2022-11-25
      • 2019-12-18
      • 1970-01-01
      • 2021-07-08
      • 1970-01-01
      • 2020-01-17
      • 2021-11-04
      • 1970-01-01
      相关资源
      最近更新 更多