Javascript Angular2表单:具有相关字段的验证器
给定一个可以输入城市名称或其纬度和经度的表单。 表单将验证是否填写了城市名称,或者是否同时填写了纬度和经度。纬度和经度(如果已填充)必须是数字 我可以用这三个字段创建一个Javascript Angular2表单:具有相关字段的验证器,javascript,angular,angular2-forms,Javascript,Angular,Angular2 Forms,给定一个可以输入城市名称或其纬度和经度的表单。 表单将验证是否填写了城市名称,或者是否同时填写了纬度和经度。纬度和经度(如果已填充)必须是数字 我可以用这三个字段创建一个FormGroup,然后做一个自定义验证器 function fatValidator(group: FormGroup) { // if cityName is present : is valid // else if lat and lng are numbers : is valid // el
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;
}
}
}
这将使'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;
}
}
static conditionalUnrequired(conditionalControlKey:string,ControlKey:Array):验证器fn{
return(control:FormControl):{[key:string]:any}=>{
让formGroup=control.root;
if(formGroup实例的formGroup){
if(controlKeys.every)(项目)=>{
返回表单组。包含(项);
})&&formGroup.contains(条件控制键)){
让firstControlHasValue=(formGroup.controls[conditionalControlKey].value instanceof Array)?formGroup.controls[conditionalControlKey].value.length>0:
!(formGroup.controls[conditionalControlKey]。值=“”),
结果=CustomValidators.controlsHaveValueCheck(controlKeys,formGroup);
formGroup.controls[conditionalControlKey].setErrors(null);//同时重置条件控件。。。
如果(firstControlHasValue&&formGroup.controls[conditionalControlKey].value!==false){//还检查false(未选中的复选框值)。。。
返回(result.every)(无效)=>{
返回无效===false;
}))?null:{必需:true};
}
}
}
返回null;
}
}
此方法将根据条件控制键
的值生成一组表单控件“必需”
,即,如果条件控制键
具有值,则控制键
数组中的所有其他控件都不需要,否则都需要
我希望这对任何人来说都不会太复杂——我相信这些代码片段可以改进,但我觉得它们恰当地展示了一种实现这一点的方法。使用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;
}
}
}
这将使'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;
}
}
static conditionalUnrequired(conditionalControlKey:string,ControlKey:Array):验证器fn{
return(control:FormControl):{[key:string]:any}=>{
让formGroup=control.root;
if(formGroup实例的formGroup){
if(controlKeys.every)(项目)=>{
返回表单组。包含(项);
})&&formGroup.contains(条件控制键)){
让firstControlHasValue=(formGroup.controls[conditionalControlKey].value instanceof Array)?formGroup.controls[conditionalControlKey].value.length>0:
!(formGroup.controls[conditionalControlKey]。值=“”),
结果=CustomValidators.controlsHaveValueCheck(controlKeys,formGroup);
formGroup.controls[conditionalControlKey].setErrors(null);//同时重置条件控件。。。
如果(firstControlHasValue&&formGroup.controls[conditionalControlKey].value!==false){//还检查false(未选中的复选框值)。。。
返回(result.every)(无效)=>{
返回无效===false;
}))?null:{必需:true};
}
}
}
返回null;
}
}
此方法将根据条件控制键
的值生成一组表单控件“必需”
,即,如果条件控制键
具有值,则控制键
数组中的所有其他控件都不需要,否则都需要
我希望这对任何人来说都不会太复杂,因为我相信这些代码片段可以改进,但我觉得它们恰当地展示了一种解决方法。你的意思是说,如果所有代码都被填充,那么它就不应该是有效的吗?在这种情况下,通过UX解决这个问题不是更好吗?UX只是将其中一个选项公开为可编辑,然后清除另一个选项,或者根据选择禁用另一个选项。我完全可以调整UX/UI以避免这个问题,但我认为这不是一个不常见的用例。设想一个表单有两个电话号码(家庭和手机),用户必须至少输入一个。每个电话字段应验证为电话号码格式,但组本身可以是val