假设我从angular2 app生成的html看起来像这样:
<app> <form [formGroup]="myForm" (ngSubmit)="onSubmit(myForm.value)"> <panel-component> <mid-component> <inner-component-with-inputs> <input/> <inner-component-with-inputs> <mid-component> </panel-component> <panel-component> <mid-component> <inner-component-with-inputs> <input/> <inner-component-with-inputs> <mid-component> </panel-component> <!-- many many many fields --> <button type="submit">Submit</button> </form> </app>
如何设置外部< form>以这种方式我可以在提交时验证所有内部输入?我是否必须通过@Input()将myForm从面板组件一直传递到内部组件与输入?或者还有其他方式吗?
在我的应用程序中,我有非常大的形式,有多个面板,子面板,标签,模态等.我需要能够在提交时立即验证它.
互联网上的所有教程和资源仅涉及跨越一个组件/模板的表单.
解决方法
当涉及父/子关系时,您将在整个Angular源代码中看到的常见模式是父类型,将自身添加为自身的提供者.这样做是允许子组件注入父组件.由于
hierarchical DI,在组件树中只会有一个父组件的实例.下面是一个可能看起来像的例子
export abstract class FormControlContainer { abstract addControl(name: string,control: FormControl): void; abstract removeControl(name: string): void; } export const formGroupContainerProvider: any = { provide: FormControlContainer,useExisting: forwardRef(() => NestedFormComponentsComponent) }; @Component({ selector: 'nested-form-components',template: ` ... `,directives: [REACTIVE_FORM_DIRECTIVES,ChildComponent],providers: [formGroupContainerProvider] }) export class ParentComponent implements FormControlContainer { form: FormGroup = new FormGroup({}); addControl(name: string,control: FormControl) { this.form.addControl(name,control); } removeControl(name: string) { this.form.removeControl(name); } }
一些说明:
>我们使用接口/抽象父(FormControlContainer)有几个原因
>它将ParentComponent与ChildComponent分离.孩子不需要知道关于特定ParentComponent的任何信息.所有它知道的是FormControlContainer和合同.
>我们只通过接口契约在ParentComponent上公开需要的方法.
>我们只将ParentComponent宣传为FormControlContainer,因此后者是我们将注入的内容.
>我们以formControlContainerProvider的形式创建提供程序,然后将该提供程序添加到ParentComponent.由于分层DI,现在所有孩子都可以访问父母.
>如果您不熟悉forwardRef,this is a great article
现在,你可以做孩子
@Component({ selector: 'child-component',directives: [REACTIVE_FORM_DIRECTIVES] }) export class ChildComponent implements OnDestroy { firstName: FormControl; lastName: FormControl; constructor(private _parent: FormControlContainer) { this.firstName = new FormControl('',Validators.required); this.lastName = new FormControl('',Validators.required); this._parent.addControl('firstName',this.firstName); this._parent.addControl('lastName',this.lastName); } ngOnDestroy() { this._parent.removeControl('firstName'); this._parent.removeControl('lastName'); } }
IMO,这比通过@Inputs传递FormGroup要好得多.如前所述,这是Angular源代码中的常见设计,因此我认为可以肯定地说这是一种可接受的模式.
如果要使子组件更可重用,可以创建构造函数参数@Optional().
以下是我用来测试上述例子的完整资料
import { Component,OnInit,ViewChildren,QueryList,OnDestroy,forwardRef,Injector } from '@angular/core'; import { FormControl,FormGroup,ControlContainer,Validators,FormGroupDirective,REACTIVE_FORM_DIRECTIVES } from '@angular/forms'; export abstract class FormControlContainer { abstract addControl(name: string,template: ` <form [formGroup]="form"> <child-component></child-component> <div> <button type="button" (click)="onSubmit()">Submit</button> </div> </form> `,forwardRef(() => ChildComponent)],providers: [formGroupContainerProvider] }) export class NestedFormComponentsComponent implements FormControlContainer { form = new FormGroup({}); onSubmit(e) { if (!this.form.valid) { console.log('form is INVALID!') if (this.form.hasError('required',['firstName'])) { console.log('First name is required.'); } if (this.form.hasError('required',['lastName'])) { console.log('Last name is required.'); } } else { console.log('form is VALID!'); } } addControl(name: string,control: FormControl): void { this.form.addControl(name,control); } removeControl(name: string): void { this.form.removeControl(name); } } @Component({ selector: 'child-component',template: ` <div> <label for="firstName">First name:</label> <input id="firstName" [formControl]="firstName" type="text"/> </div> <div> <label for="lastName">Last name:</label> <input id="lastName" [formControl]="lastName" type="text"/> </div> `,this.lastName); } ngOnDestroy() { this._parent.removeControl('firstName'); this._parent.removeControl('lastName'); } }