如何找到无效的角度(v2以上)反应形式的控制

我有一个角度的反应形式如下:

this.AddCustomerForm = this.formBuilder.group({
Firstname: ['', Validators.required],
Lastname: ['', Validators.required],
Email: ['', Validators.required, Validators.pattern(this.EMAIL_REGEX)],
Picture: [''],
Username: ['', Validators.required],
Password: ['', Validators.required],
Address: ['', Validators.required],
Postcode: ['', Validators.required],
City: ['', Validators.required],
Country: ['', Validators.required]
});


createCustomer(currentCustomer: Customer)
{
if (!this.AddCustomerForm.valid)
{
//some app logic
}
}

有效的返回 false,但是看起来一切正常。

我试图通过检查控件集合中的 status 属性来查找。但我想知道是否有一种方法可以找到无效的,并显示给用户?

176730 次浏览

您可以简单地迭代每个控件并检查状态:

    public findInvalidControls() {
const invalid = [];
const controls = this.AddCustomerForm.controls;
for (const name in controls) {
if (controls[name].invalid) {
invalid.push(name);
}
}
return invalid;
}

窗体和所有控件都扩展了角度类 AbstractControl。每个实现都有一个验证错误的访问器。

let errors = this.AddCustomerForm.errors
// errors is an instance of ValidatorErrors

Api 文档包含所有引用 Https://angular.io/api/forms/abstractcontrol

剪辑

我认为错误访问器是这样工作的,但是这个到 github 的链接表明有一些其他人和我想的一样 Https://github.com/angular/angular/issues/11530

在任何情况下,通过使用控件访问器,您都可以迭代窗体中的所有窗体控件。

Object.keys(this.AddCustomerForm.controls)
.forEach( control => {
//check each control here
// if the child is a formGroup or a formArray
// you may cast it and check it's subcontrols too
})

如果表单中没有太多字段,那么只需将 F12悬停在控件上,就可以看到带有字段的原始/触摸/有效值-的弹出窗口 “ # fieldname.form-control. ng-untouch ed.ng-void”。

我擅自改进了 Angularindepth.com-s 代码,这样它也可以递归地搜索嵌套形式的无效输入。它是否由 FormArray-s 或 FormGroup-s 嵌套。只需输入顶级 formGroup,它就会返回所有无效的 FormControls。

如果您将 FormControl 检查和无效数组功能添加分离到一个单独的函数中,那么您可以略过一些“ instanceof”类型检查。这会使函数看起来更简洁,但是我需要一个全局的、单一的函数选项来获得所有无效形式的平面数组 Controls,这就是解决方案!

findInvalidControls( _input: AbstractControl, _invalidControls: AbstractControl[] ): AbstractControl[] {
if ( ! _invalidControls ) _invalidControls = [];
if ( _input instanceof FormControl  ) {
if ( _input.invalid ) _invalidControls.push( _input );
return _invalidControls;
}


if ( ! (_input instanceof FormArray) && ! (_input instanceof FormGroup) ) return _invalidControls;


const controls = _input.controls;
for (const name in controls) {
let control = controls[name];
switch( control.constructor.name )
{
case 'AbstractControl':
case 'FormControl':
if (control.invalid) _invalidControls.push( control );
break;


case 'FormArray':
(<FormArray> control ).controls.forEach( _control => _invalidControls = findInvalidControls( _control, _invalidControls ) );
break;


case 'FormGroup':
_invalidControls = findInvalidControls( control, _invalidControls );
break;
}
}


return _invalidControls;
}

只针对那些需要它的人,这样他们就不用自己编码了。

编辑 # 1

它还被要求返回无效的 FormArray-s 和 FormGroup,因此如果您也需要它,请使用下面的代码

findInvalidControls( _input: AbstractControl, _invalidControls: AbstractControl[] ): AbstractControl[] {
if ( ! _invalidControls ) _invalidControls = [];
if ( _input instanceof FormControl  ) {
if ( _input.invalid ) _invalidControls.push( _input );
return _invalidControls;
}


if ( ! (_input instanceof FormArray) && ! (_input instanceof FormGroup) ) return _invalidControls;


const controls = _input.controls;
for (const name in controls) {
let control = controls[name];
if (control.invalid) _invalidControls.push( control );
switch( control.constructor.name )
{
case 'FormArray':
(<FormArray> control ).controls.forEach( _control => _invalidControls = findInvalidControls( _control, _invalidControls ) );
break;


case 'FormGroup':
_invalidControls = findInvalidControls( control, _invalidControls );
break;
}
}


return _invalidControls;
}

我刚刚解决了这个问题: 每个表单字段都是有效的,但是表单本身仍然是无效的。

结果是我在 FormArray 上设置了‘ Validator.need’,其中动态添加/删除控件。因此,即使 FormArray 为空,它仍然是必需的,因此表单总是无效的,即使每个可见控件都被正确填充。

我没有找到表单的无效部分,因为我的“ findInvalidControls”函数只检查了 FormControl 的,而没有检查 FormGroup/FormArray。所以我稍微更新了一下:

/*
Returns an array of invalid control/group names, or a zero-length array if
no invalid controls/groups where found
*/
public findInvalidControlsRecursive(formToInvestigate:FormGroup|FormArray):string[] {
var invalidControls:string[] = [];
let recursiveFunc = (form:FormGroup|FormArray) => {
Object.keys(form.controls).forEach(field => {
const control = form.get(field);
if (control.invalid) invalidControls.push(field);
if (control instanceof FormGroup) {
recursiveFunc(control);
} else if (control instanceof FormArray) {
recursiveFunc(control);
}
});
}
recursiveFunc(formToInvestigate);
return invalidControls;
}

可以记录表单 console.log(this.addCustomerForm.value)的值,它将控制所有控件的值然后空或“”(空)字段指示无效的控件

无效的 Angular 控件具有名为 无效的 CSS 类。

在“ Chrome 中的 DevTools”下,选择“控制台”选项卡。

在控制台提示符下运行以下命令,以获得无效的 Angular 控件,这些控件带有 CSS 类‘ ng- 无效’

document.getElementsByClassName('ng-invalid')

产出应与此类似: enter image description here

在这种情况下,带下划线的文本用于窗体控件 listen-address和被包围的文本: .ng-invalid表示控件无效。

注: 测试铬

我认为您应该尝试使用 this.form.updateValueAndValidity()或尝试在每个控件中执行相同的方法。

试试这个

 findInvalidControls(f: FormGroup) {
const invalid = [];
const controls = f.controls;
for (const name in controls) {
if (controls[name].invalid) {
invalid.push(name);
}
}
return invalid;
}

现在,在角9中,您可以使用 markAllAsTouch ()方法来显示无效的控件验证器:

this.AddCustomerForm.markAllAsTouched();

在反应形式的每个控件上都存在一个 .error属性。如果此 .error设置为 true,则表示控件无效。因此,循环遍历控件并检查这个 .error字段将让我们知道哪些字段/控件是无效的。

下面的代码将记录所有无效的控件

for (let el in this.ReactiveForm.controls) {
if (this.ReactiveForm.controls[el].errors) {
console.log(el)
}
}

也可以将字段名附加到数组或字符串,并指示用户哪些字段无效

检查 html 页面中的空或空表单控件值

表单控件值: { formname.value | json }}

上述问题的解决方案的更清晰和不可改变的递归版本:

附注: 两种方法你都需要。

测试直到角度11

如果编译器抱怨 latMap,请参考这个(平面地图,平面,平面不存在于任何类型[]) ,不要忘记重新启动 ng serve

findInvalidControls(controls = this.defaultFormGroup.controls) {
const ctrls = Object.values(controls);
const names = Object.keys(controls);
return ctrls.map((a,i) => [a, i])
.filter(a => (a[0] as FormControl).invalid)
.flatMap(a => {
if (a[0] instanceof FormArray) {
return this.findInvalidArrayControls(a[0].controls);
} else if (a[0] instanceof FormGroup) {
return this.findInvalidControls(a[0].controls);
} else {
return names[a[1] as number];
}
});
}


findInvalidArrayControls(controls: AbstractControl[]) {
const ctrls = Object.values(controls);
const names = Object.keys(controls);
return ctrls.map((a,i) => [a, i])
.filter(a => (a[0] as FormControl).invalid)
.flatMap(a => {
if (a[0] instanceof FormArray) {
return this.findInvalidArrayControls(a[0].controls);
} else if (a[0] instanceof FormGroup) {
return this.findInvalidControls(a[0].controls);
}
else {
return names[a[1] as number];
}
});
}


在我的例子中,我禁用了所有的表单控件。

这似乎是一个开放的错误,在角度: https://github.com/angular/angular/issues/39287

所以我也对抗了这条龙。 就像我这个勇敢的骑士一样,我首先收集了 我的武器,阅读了 地图,然后与这个可怕的野兽战斗。

注意

对于复杂的形式或结构来说,这不是一个可以接受的答案,但是我发现它适用于简单的形式或结构,没有太多的复杂性

守则的作用如下:

  • 以数组形式获取窗体控件
  • 循环检查表单控制是否无效
  • 如果无效,过滤器将包括它
  • 如果有无效的,结果数组将用该控件填充
  • 如果这个结果的长度等于0,我们可以声明没有控件是无效的,因此整个表单是有效的
isFormValid = () :boolean =>
Object.values(this.form.controls)
.filter(c => c.invalid).length === 0


// or single lined
isFormValid = () :boolean => Object.values(this.form.controls).filter(c => c.invalid).length === 0


你可以在你想要的地方使用它,在提交按钮上,在提交或在你自己的甜蜜点上。

创建标志

inProcess: boolean= false
   

this.AddCustomerForm = this.formBuilder.group({
Firstname: ['', Validators.required],
Lastname: ['', Validators.required],
Username: ['', Validators.required],
Password: ['', Validators.required],
Address: ['', Validators.required],
Postcode: ['', Validators.required],
City: ['', Validators.required],
Country: ['', Validators.required]
});


onSubmit()
{
if(this.AddCustomerForm.invalid)
{
return
}


this.inProcess = true


// pass form value to restapi


}

以及在 HTML 表单中使用的 inProcess 标志禁用按钮

<button mat-button [disable]="inProcess"> ADD </button>

一旦所有的表单值都是正确的,那么只有 ADD 按钮是可见的

希望对你们有所帮助! ! !

对于任何需要过滤 formArray 的人来说,只有有效的控件才是解决方案;

const validControls = this.participantsDetails.controls.filter(control => control.valid);

当然,对于无效的;

const validControls = this.participantsDetails.controls.filter(control => control.invalid);

对 FormGroup 和 FormArray 有效。此方法返回无效的控件名称。

private getInvalidControlNames(input: FormGroup | FormArray): string[] {
let invalidControlNames: string[] = [];
Object.keys(input.controls).forEach((controlName) => {
const control = input.get(controlName)!;
if (control.invalid && control instanceof FormControl) {
invalidControlNames.push(controlName);
} else if (
control.invalid &&
(control instanceof FormGroup || control instanceof FormArray)
) {
invalidControlNames.push(...this.getInvalidControlNames(control));
}
});
return [...new Set(invalidControlNames)];
}