我正在研究Angular反应形式验证。我在上面输入了带有Google自动完成功能的输入:
<input autocorrect="off" autocapitalize="off" spellcheck="off" type="text" class="input-auto input" formControlName="address">
这是标准的实现方式,每当您输入一些关键字,您都会获得位置建议:
现在,我想做的就是验证地址。 该地址需要包含邮政编码-只有这样才能有效。因此,一旦您键入内容并选择建议之一,就应该触发验证。选择的建议中包含邮政编码有效,否则无效-无效。
如您所见,整个地址只有一个表单控件(应该保持这样),我用Google API中的格式化地址填充了该控件。我还从Google Places API获取有关地址组件的信息,这些信息存储在全局变量(zipCode
,countryName
,cityName
,streetName
)中:>
this.mapsAPILoader.load().then(() => {
const autocomplete = new window['google'].maps.places.Autocomplete(this.searchElementToRef.nativeElement, {
types: ['address']
});
autocomplete.addListener('place_changed', () => {
this.ngZone.run(() => {
const place = autocomplete.getPlace();
if (place.geometry === undefined || place.geometry === null) {
return;
}
this.form.get('address').setValue(place.formatted_address);
for (const addressComponent of place.address_components) {
for (const addressComponentType of addressComponent.types) {
switch (addressComponentType) {
case 'postal_code':
this.zipCode = addressComponent.long_name;
break;
case 'country':
this.countryName = addressComponent.long_name;
break;
case 'locality':
this.cityName = addressComponent.long_name;
break;
case 'route':
this.streetName = addressComponent.long_name;
break;
}
}
}
});
});
});
在使用FormBuilder
创建表单的方法中,我使用了自定义验证程序:
public createFormGroup(): FormGroup {
return this.fb.group({
address: [null, this.zipCodeValidator()]
});
}
使用以下自定义验证方法,当地址中缺少zipCode
时,我会收到错误消息:
public zipCodeValidator(): ValidatorFn {
return (control: AbstractControl): Observable<{ [key: string]: boolean } | null> => {
if (this.zipCode !== undefined) {
return of({ zipCode: true });
}
return null;
};
}
然而,由于我无论得到的地址有无邮政编码,该表单均未正确验证,因此始终有效:
如果我将条件表单控件的相关值用作条件,则验证有效。因此,当没有输入任何内容时,将验证方法更新为以下状态会产生错误:
public zipCodeValidator(): ValidatorFn {
return (control: AbstractControl): Observable<{ [key: string]: boolean } | null> => {
if (control.value === null) {
return of({ noValue: true });
}
return null;
};
}
问题:
是否有可能像这样验证表单-条件实际上不在表单范围内(因为我没有将此zipCode
值直接传递给任何表单控件)?
答案 0 :(得分:0)
答案是,可以根据表格之外的条件进行验证,而且通常情况下,该条件与我非常接近。
首先,我搞砸了验证方法。我使用过的Observable
用于async-validation。因此,修复它之后,该方法将仅返回对象,而不返回Observable:
public zipCodeValidator(): ValidatorFn {
return (control: AbstractControl): { [key: string]: boolean } | null => {
if (control.value !== null) {
// if value exists, we can add number of conditions
if (this.zipCode !== undefined) {
return { zipCode: true };
}
} else {
// here we can react when there is no value entered - act as validator.required
return { empty: true}
}
return null;
};
}
然后,我开始完成验证,但对于zipCode
变量的先前状态。我想是因为也许我需要使用异步验证,但是它甚至更简单。我设置地址表单控件的值还为时过早:this.form.get('address').setValue(place.formatted_address);
因此,我将其移到了我要查找的邮政编码所在部分的后面,并且有效:
this.mapsAPILoader.load().then(() => {
const autocomplete = new window['google'].maps.places.Autocomplete(this.searchElementToRef.nativeElement, {
types: ['address']
});
autocomplete.addListener('place_changed', () => {
this.ngZone.run(() => {
const place = autocomplete.getPlace();
if (place.geometry === undefined || place.geometry === null) {
return;
}
for (const addressComponent of place.address_components) {
for (const addressComponentType of addressComponent.types) {
switch (addressComponentType) {
case 'postal_code':
this.zipCode = addressComponent.long_name;
break;
case 'country':
this.countryName = addressComponent.long_name;
break;
case 'locality':
this.cityName = addressComponent.long_name;
break;
case 'route':
this.streetName = addressComponent.long_name;
break;
}
}
}
this.form.get('address').setValue(place.formatted_address);
});
});
});
还有一个问题,就是我是否想在其他时候而不是从一开始就启动验证。然后只需将其设置在正确的位置并更新验证器:
this.form.get('address').setValidators(this.zipCodeValidator());
this.form.get('address').updateValueAndValidity();
如果需要在某个时候将其删除,这是简单的方法:
this.form.get('address').clearValidators();