[英]Angular2 forms : validator with interrelated fields
給定一種形式,可以輸入城市名稱或經度和緯度。 表格將驗證城市名稱是否已填寫或者是否填寫了緯度和經度 。 緯度和經度,如果填寫,必須是數字。
我可以使用這三個字段創建一個FormGroup
並執行一個自定義驗證器...
function fatValidator(group: FormGroup) {
// if cityName is present : is valid
// else if lat and lng are numbers : is valid
// else : is not valid
}
builder.group({
cityName: [''],
lat: [''],
lng: ['']
},
{
validators: fatValidator
});
...但我想利用驗證器組合(例如,在一個驗證器中測試緯度和經度是字段級別的有效數字,並在另一個驗證器中測試組級別的相互關系)。
我已經測試了幾個選項,但我堅持認為如果一個組的所有字段都有效,則該組有效。 以下結構似乎不是解決問題的正確方法:
function isNumber(control: FormControl) { ... }
function areAllFilled(group: FormGroup) { ... }
function oneIsFilledAtLeast(group: FormGroup) { ... }
builder.group({
cityName: [''],
localisation: builder.group({
lat: ['', Validators.compose([Validators.minLength(1), isNumber])],
lng: ['', Validators.compose([Validators.minLength(1), isNumber])]
},
{
validators: areAllFilled
})
},
{
validators: oneIsFilledAtLeast
});
你怎么用Angular2做到這一點甚至可能嗎?
以下是如何實現fatValidator
的示例。 正如您所看到的,它不是可重用的,而且比組合驗證器更難測試:
function fatValidator (group: FormGroup) {
const coordinatesValidatorFunc = Validators.compose([
Validators.required,
CustomValidators.isNumber
]);
const cityNameControl = group.controls.cityName;
const latControl = group.controls.lat;
const lngControl = group.controls.lng;
const cityNameValidationResult = Validators.required(cityNameControl);
const latValidationResult = coordinatesValidatorFunc(latControl);
const lngValidationResult = coordinatesValidatorFunc(lngControl);
const isCityNameValid = !cityNameValidationResult;
const isLatValid = !latValidationResult;
const isLngValid = !lngValidationResult;
if (isCityNameValid) {
return null;
}
if (isLatValid && isLngValid) {
return null;
}
if (!isCityNameValid && !isLatValid && !isLngValid) {
return { cityNameOrCoordinatesRequired: true, latAndLngMustBeNumbers: true };
}
return Object.assign({},
{ cityName: cityNameValidationResult },
{ lat: latValidationResult },
{ lng: lngValidationResult }
);
}
使用Angular的最終版本或新版本,我編寫了一個可重用的方法來向一組給定的控件添加條件必需或其他驗證。
export class CustomValidators {
static controlsHaveValueCheck(controlKeys: Array<string>, formGroup: FormGroup): Array<boolean> {
return controlKeys.map((item) => {
// reset any errors already set (ON ALL GIVEN KEYS).
formGroup.controls[item].setErrors(null);
// Checks for empty string and empty array.
let hasValue = (formGroup.controls[item].value instanceof Array) ? formGroup.controls[item].value.length > 0 :
!(formGroup.controls[item].value === "");
return (hasValue) ? false : true;
});
}
static conditionalAnyRequired(controlKeys: Array<string>): ValidatorFn {
return (control: FormControl): {[key: string]: any} => {
let formGroup = control.root;
if (formGroup instanceof FormGroup) {
// Only check if all FormControls are siblings(& present on the nearest FormGroup)
if (controlKeys.every((item) => {
return formGroup.contains(item);
})) {
let result = CustomValidators.controlsHaveValueCheck(controlKeys, formGroup);
// If any item is valid return null, if all are invalid return required error.
return (result.some((item) => {
return item === false;
})) ? null : {required: true};
}
}
return null;
}
}
}
這可以在你的代碼中使用,如下所示:
this.form = new FormGroup({
'cityName': new FormControl('',
CustomValidators.conditionalAnyRequired(['cityName', 'lat', 'lng'])),
'lat': new FormControl('',
Validators.compose([Validators.minLength(1),
CustomValidators.conditionalAnyRequired(['cityName', 'lat', 'lng']))),
'lng': new FormControl('',
Validators.compose([Validators.minLength(1),
CustomValidators.conditionalAnyRequired(['cityName', 'lat', 'lng'])))
})
這將使任何'city'
, 'lat'
或'lng'
必需。
此外,如果您需要'city'
或'lat'
和'lng'
,則可以包含其他驗證器,例如:
static conditionalOnRequired(conditionalControlKey: string, controlKeys: Array<string>): ValidatorFn {
return (control: FormControl): {[key: string]: any} => {
let formGroup = control.root;
if (formGroup instanceof FormGroup) {
if (controlKeys.every((item) => {
return formGroup.contains(item);
}) && formGroup.contains(conditionalControlKey)) {
let firstControlHasValue = (formGroup.controls[conditionalControlKey].value instanceof Array) ? formGroup.controls[conditionalControlKey].value.length > 0 :
!(formGroup.controls[conditionalControlKey].value === ""),
result = CustomValidators.controlsHaveValueCheck(controlKeys, formGroup);
formGroup.controls[conditionalControlKey].setErrors(null); // Also reset the conditional Control...
if (firstControlHasValue && formGroup.controls[conditionalControlKey].value !== false) {// also checks for false (for unchecked checkbox value)...
return (result.every((invalid) => {
return invalid === false;
})) ? null : {required: true};
}
}
}
return null;
}
}
此方法將根據conditionalControlKey
的值生成一組表單控件' required
',即如果conditionalControlKey
具有值,則不需要controlKeys
Array中的所有其他控件,否則全部都是必需的。
我希望這對任何人都不會過於復雜 - 我確信這些代碼片段可以改進,但我覺得它們恰當地證明了這種方法。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.