通过验证将表单分成多个组件
Dividing a form into multiple components with validation
我想用 angular 2 创建一个大表单。但是我想用多个组件创建这个表单,如下例所示。
应用组件
<form novalidate #form1="ngForm" [formGroup]="myForm">
<div>
<address></address>
</div>
<div>
<input type="text" ngModel required/>
</div>
<input type="submit" [disabled]="!form1.form.valid" > </form>
地址组件
<div>
<input type="text" ngModel required/> </div>
当我使用上面的代码时,它在我需要的浏览器中可见,但是当我删除地址组件中的文本时,提交按钮没有被禁用。
但是当我删除应用程序组件中输入框中的文本时,该按钮被正确禁用。
根据我的经验,这种表单域组合很难用模板驱动的表单来制作。地址组件中嵌入的字段不会在表单(NgForm.controls 对象)中注册,因此在验证表单时不会考虑它们。
- 您可以创建一个包含所有验证的 ControlValueAccessor 组件(接受 ngModel 属性),但是这样就很难显示验证错误和传播更改(地址被视为具有复杂值的单个表单字段)。
- 您可以将表单引用传递到地址组件并在其中注册您的内部控件,但我没有尝试过,而且似乎是一种奇怪的方法(我在任何地方都没有看到过)。
- 您可以切换到反应式表单(而不是模板驱动),将表单组对象(表示地址)传递到地址组件中,将验证保留在表单定义中。您可以在此处查看示例 https://scotch.io/tutorials/how-to-build-nested-model-driven-forms-in-angular-2
我会使用一种效果很好的反应式形式,关于您的评论:
Is there any other simple example for this one? Maybe the same example without loops
我可以举个例子。您需要做的就是嵌套 FormGroup
并将其传递给 child.
假设您的表单如下所示,并且您想将 address
表单组传递给 child:
ngOnInit() {
this.myForm = this.fb.group({
name: [''],
address: this.fb.group({ // create nested formgroup to pass to child
street: [''],
zip: ['']
})
})
}
然后在你的 parent 中,传递嵌套的表单组:
<address [address]="myForm.get('address')"></address>
在您的 child 中,对嵌套表单组使用 @Input
:
@Input() address: FormGroup;
并在您的模板中使用 [formGroup]
:
<div [formGroup]="address">
<input formControlName="street">
<input formControlName="zip">
</div>
如果您不想创建实际的嵌套表单组,则不需要这样做,您可以将 parent 表单传递给 child,因此如果您的表单看起来像:
this.myForm = this.fb.group({
name: [''],
street: [''],
zip: ['']
})
你可以通过任何你想要的控制。使用与上面相同的示例,我们只想显示 street
和 zip
,child 组件保持不变,但模板中的 child 标签看起来像:
<address [address]="myForm"></address>
这是一个
Demo of first option, here's the second Demo
有关嵌套 model-driven 表单的更多信息 here。
我想分享在我的案例中完成工作的方法。我创建了以下指令:
import { Directive } from '@angular/core';
import { ControlContainer, NgForm } from '@angular/forms';
@Directive({
selector: '[appUseParentForm]',
providers: [
{
provide: ControlContainer,
useFactory: function (form: NgForm) {
return form;
},
deps: [NgForm]
}
]
})
export class UseParentFormDirective {
}
现在,如果您在子组件上使用此指令,例如:
<address app-use-parent-form></address>
AddressComponent 中的控件将添加到 form1。因此,表单有效性也将取决于子组件内控件的状态。
仅检查 Angular 6
在模板驱动的表单中也有一种方法可以做到这一点。 ngModel 在每个组件上自动创建一个单独的表单,但您可以通过将此添加到您的组件来注入父组件的表单:
@Component({
viewProviders: [{ provide: ControlContainer, useExisting: NgForm}]
}) export class ChildComponent
不过,您必须确保每个输入都有一个唯一的名称。所以如果你使用 *ngFor 调用你的子组件,你必须将索引(或任何其他唯一标识符)放入 name 中,例如:
[name]="'address_' + i"
如果你想将表单结构化为 FormGroups,你可以使用 ngModelGroup 和
viewProviders: [{ provide: ControlContainer, useExisting: NgModelGroup }]
代替 ngForm 并添加
[ngModelGroup]="yourNameHere"
到您的一些包含标签的子组件 html。
使用最新版本的 Angular 2+,您可以将表单放入多个组件中,而无需将 formGroup 实例作为输入传递给子组件。
您可以通过将此提供程序导入您的子组件来实现此目的:
viewProviders: [{provide: ControlContainer,useExisting: FormGroupDirective}]
然后您也可以通过注入 formGroupDirective 来访问主窗体:
constructor(private readonly formGroupDirective: FormGroupDirective)
我想用 angular 2 创建一个大表单。但是我想用多个组件创建这个表单,如下例所示。
应用组件
<form novalidate #form1="ngForm" [formGroup]="myForm">
<div>
<address></address>
</div>
<div>
<input type="text" ngModel required/>
</div>
<input type="submit" [disabled]="!form1.form.valid" > </form>
地址组件
<div>
<input type="text" ngModel required/> </div>
当我使用上面的代码时,它在我需要的浏览器中可见,但是当我删除地址组件中的文本时,提交按钮没有被禁用。
但是当我删除应用程序组件中输入框中的文本时,该按钮被正确禁用。
根据我的经验,这种表单域组合很难用模板驱动的表单来制作。地址组件中嵌入的字段不会在表单(NgForm.controls 对象)中注册,因此在验证表单时不会考虑它们。
- 您可以创建一个包含所有验证的 ControlValueAccessor 组件(接受 ngModel 属性),但是这样就很难显示验证错误和传播更改(地址被视为具有复杂值的单个表单字段)。
- 您可以将表单引用传递到地址组件并在其中注册您的内部控件,但我没有尝试过,而且似乎是一种奇怪的方法(我在任何地方都没有看到过)。
- 您可以切换到反应式表单(而不是模板驱动),将表单组对象(表示地址)传递到地址组件中,将验证保留在表单定义中。您可以在此处查看示例 https://scotch.io/tutorials/how-to-build-nested-model-driven-forms-in-angular-2
我会使用一种效果很好的反应式形式,关于您的评论:
Is there any other simple example for this one? Maybe the same example without loops
我可以举个例子。您需要做的就是嵌套 FormGroup
并将其传递给 child.
假设您的表单如下所示,并且您想将 address
表单组传递给 child:
ngOnInit() {
this.myForm = this.fb.group({
name: [''],
address: this.fb.group({ // create nested formgroup to pass to child
street: [''],
zip: ['']
})
})
}
然后在你的 parent 中,传递嵌套的表单组:
<address [address]="myForm.get('address')"></address>
在您的 child 中,对嵌套表单组使用 @Input
:
@Input() address: FormGroup;
并在您的模板中使用 [formGroup]
:
<div [formGroup]="address">
<input formControlName="street">
<input formControlName="zip">
</div>
如果您不想创建实际的嵌套表单组,则不需要这样做,您可以将 parent 表单传递给 child,因此如果您的表单看起来像:
this.myForm = this.fb.group({
name: [''],
street: [''],
zip: ['']
})
你可以通过任何你想要的控制。使用与上面相同的示例,我们只想显示 street
和 zip
,child 组件保持不变,但模板中的 child 标签看起来像:
<address [address]="myForm"></address>
这是一个
Demo of first option, here's the second Demo
有关嵌套 model-driven 表单的更多信息 here。
我想分享在我的案例中完成工作的方法。我创建了以下指令:
import { Directive } from '@angular/core';
import { ControlContainer, NgForm } from '@angular/forms';
@Directive({
selector: '[appUseParentForm]',
providers: [
{
provide: ControlContainer,
useFactory: function (form: NgForm) {
return form;
},
deps: [NgForm]
}
]
})
export class UseParentFormDirective {
}
现在,如果您在子组件上使用此指令,例如:
<address app-use-parent-form></address>
AddressComponent 中的控件将添加到 form1。因此,表单有效性也将取决于子组件内控件的状态。
仅检查 Angular 6
在模板驱动的表单中也有一种方法可以做到这一点。 ngModel 在每个组件上自动创建一个单独的表单,但您可以通过将此添加到您的组件来注入父组件的表单:
@Component({
viewProviders: [{ provide: ControlContainer, useExisting: NgForm}]
}) export class ChildComponent
不过,您必须确保每个输入都有一个唯一的名称。所以如果你使用 *ngFor 调用你的子组件,你必须将索引(或任何其他唯一标识符)放入 name 中,例如:
[name]="'address_' + i"
如果你想将表单结构化为 FormGroups,你可以使用 ngModelGroup 和
viewProviders: [{ provide: ControlContainer, useExisting: NgModelGroup }]
代替 ngForm 并添加
[ngModelGroup]="yourNameHere"
到您的一些包含标签的子组件 html。
使用最新版本的 Angular 2+,您可以将表单放入多个组件中,而无需将 formGroup 实例作为输入传递给子组件。
您可以通过将此提供程序导入您的子组件来实现此目的:
viewProviders: [{provide: ControlContainer,useExisting: FormGroupDirective}]
然后您也可以通过注入 formGroupDirective 来访问主窗体:
constructor(private readonly formGroupDirective: FormGroupDirective)