如何为实现Validator的自定义ControlValueAccessor输入组件传递验证器?
自定义手机号输入组件验证状态异常问题
我正在开发一个可全应用复用的自定义手机号输入组件,包含国家码选择器和手机号输入框。已经实现了ControlValueAccessor,能作为单个值在表单中使用,但外部传入的Validators无法更新验证状态,required错误始终存在。
因为使用Ionic组件库,其输入组件样式依赖关联表单控件的有效性,所以我通过内部FormControl管理组件值,尝试同步整体控件的有效性,但问题仍未解决。
phone-input.component.ts
import { Component, Input, OnInit, ViewChild } from '@angular/core'; import { ControlValueAccessor, FormControl, NG_VALIDATORS, NG_VALUE_ACCESSOR, Validator } from '@angular/forms'; import parsePhoneNumber from 'libphonenumber-js'; @Component({ selector: 'app-phone-input', templateUrl: './phone-input.component.html', styleUrls: ['./phone-input.component.scss'], providers: [ { provide: NG_VALUE_ACCESSOR, useExisting: PhoneInputComponent, multi: true, }, { provide: NG_VALIDATORS, useExisting: PhoneInputComponent, multi: true, }, ], }) export class PhoneInputComponent implements ControlValueAccessor, Validator { countryCode: FormControl = new FormControl(''); nationalNumber: FormControl = new FormControl(''); constructor() { } validate(control: FormControl) { const isValid = !!this.nationalNumber.value && !!this.countryCode.value; // control的状态、有效性、错误信息从未更新,除非返回新内容 // 能看到异步验证器触发,但结果未关联到传入的FormControl参数 // 即使已设置值,{ required: true }错误始终存在 console.log(control); console.log(control.status); console.log(control.valid); console.log(control.errors); const allErrors = { ...control.errors, // ...{ invalid: !isValid }, } console.log(allErrors) this.nationalNumber.setErrors(allErrors); return allErrors; } writeValue(value: string) { const phoneNumber = parsePhoneNumber(value); if (phoneNumber) { this.countryCode.setValue(phoneNumber.countryCallingCode); this.nationalNumber.setValue(phoneNumber.nationalNumber); } } onChange: any = () => { }; onTouched: any = () => { }; myChanged(event: Event) { this.onChange('+' + this.countryCode.value + this.nationalNumber.value); } myTouched(event: Event) { this.onTouched('+' + this.countryCode.value + this.nationalNumber.value); } registerOnChange(fn: any) { this.onChange = fn; } registerOnTouched(fn: any): void { this.onTouched = fn; } setDisabledState(isDisabled: boolean): void { if (isDisabled) { this.countryCode.disable(); this.nationalNumber.disable(); } else { this.countryCode.enable(); this.nationalNumber.enable(); } } formatMap: { [country: string]: Format } = { 'US': { countryCodeValue: "1", countryCodeLabel: "🇺🇸 +1", nationalNumberMask: "(000) 000-0000", // TODO: 实现掩码 }, } get countryList() { return Object.keys(this.formatMap); } } interface Format { countryCodeValue: string; countryCodeLabel: string; nationalNumberMask: string; }
phone-input.component.html
<div class="phone-input-container"> <div class="country-code-input"> <select [formControl]="countryCode" (change)="myChanged($event)"> <option *ngFor="let country of countryList" [value]="formatMap[country].countryCodeValue"> {{formatMap[country].countryCodeLabel}} </option> </select> </div> <div class="phone-number-input"> <input type="number" [formControl]="nationalNumber" (input)="myChanged($event)" (blur)="myTouched($event)"> </div> </div>
login.component.ts
import { Component, OnInit } from '@angular/core'; import { FormControl, FormGroup, Validators } from '@angular/forms'; @Component({ selector: 'app-login', templateUrl: './login.component.html', styleUrls: ['./login.component.scss'], }) export class LoginComponent implements OnInit { loginForm = new FormGroup({ phone: new FormControl('', { validators: [ Validators.required ], asyncValidators: [ uniqueFieldValidator('phoneNumber') ], updateOn: 'blur', }), }); constructor() { } ngOnInit() { } onSubmit() { console.log(this.phone); } get phone() { return this.loginForm.get('phone') as FormControl; } }
login.component.html
<form [formGroup]="loginForm" (ngSubmit)="onSubmit()"> <app-phone-input id="phone" formControlName="phone"></app-phone-input> </form>
内容的提问来源于stack exchange,提问作者Galen Howlett
相关产品推荐
相关产品推荐

