2016-07-28 53 views
4

比方说,从angular2应用我生成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>?我是否必须通过myForm@Input()一路从panel-component一路下降到inner-component-with-inputs?或者有其他方法吗?

在我的应用程序中,我有非常大的窗体,包含多个面板,子面板,选项卡,模态等,我需要能够在提交时一次性验证它。

互联网上的所有教程和资源只讨论跨越一个组件/模板的表单。

+0

如果你还在寻找一个答案,我发现这对我有帮助,特别是在卡拉的评论中发现的掠夺者:https://github.com/angular/angular/issues/9281#issuecomm ent-238610807 – cbros2008

回答

10

当涉及父/子关系时,您将在整个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)为一对夫妇的原因

    1. 它将ParentComponentChildComponent中分离出来。孩子不需要知道任何关于具体的ParentComponent。所有它知道的是FormControlContainer和合同。
    2. 我们只通过接口合约在ParentComponent上公开方法。
  • 我们只有做广告ParentComponentFormControlContainer,那么后者就是我们将注入。

  • 我们以formControlContainerProvider的形式创建提供商,然后将该提供商添加到ParentComponent。由于分级DI,现在所有的孩子都可以访问父母。

  • 如果你不熟悉forwardRefthis is a great article

在子女

现在,你可以做

@Component({ 
    selector: 'child-component', 
    template: ` 
    ... 
    `, 
    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,这是一个比经过FormGroup更好的设计通过@Input s。如前所述,这是整个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, control: FormControl): void; 
    abstract removeControl(name: string): void; 
} 

export const formGroupContainerProvider: any = { 
    provide: FormControlContainer, 
    useExisting: forwardRef(() => NestedFormComponentsComponent) 
}; 

@Component({ 
    selector: 'nested-form-components', 
    template: ` 
    <form [formGroup]="form"> 
     <child-component></child-component> 
     <div> 
     <button type="button" (click)="onSubmit()">Submit</button> 
     </div> 
    </form> 
    `, 
    directives: [REACTIVE_FORM_DIRECTIVES, 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> 
    `, 
    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'); 
    } 
} 
+0

当我使用它时,您的代码就像您粘贴它一样,但当我尝试将ChildComponent移动到另一个文件时,我得到异常:“无法解析所有ChildComponent的参数”。你能帮我解决这个问题吗?我使用“typescript”作为转译器,并且必须在ChildComponent的构造函数中添加@Inject(FormControlContainer),但问题仍然存在。 – Celebes

+0

@Celebes将提供程序导入您的子组件,然后将其添加到提供程序数组提供程序中:[formGroupContainerProvider]' –

0

更容易formGroup和formControl传递到下部组件的方式完整的源 - 使用@Inputs。 Plunker:https://plnkr.co/edit/pd30ru?p=preview

在FormComponent(MgForms)[主]我们这样做:

this.form = this.formBuilder.group(formFields); 

模板

<form [formGroup]="form" novalidate> 

    <div class="mg-form-element" *ngFor="let element of fields"> 
    <div class="form-group"> 
     <label class="center-block">{{element.description?.label?.text}}: 

     <div [ngSwitch]="element.type"> 
      <!--textfield component--> 
      <div *ngSwitchCase="'textfield'"class="form-control"> 
      <mg-textfield 
       [group]="form" 
       [control]="form.controls[element.fieldId]" 
       [element]="element"> 
      </mg-textfield> 
      </div>  

      <!--numberfield component--> 
      <div *ngSwitchCase="'numberfield'"class="form-control"> 
      <mg-numberfield 
       [group]="form" 
       [control]="form.controls[element.fieldId]" 
       [element]="element"> 
      </mg-numberfield> 
      </div> 
     </div> 

     </label> 
    </div> 
    </div> 

</form> 

代码

在FieldComponent(MgNumberfield)内]我们这样做:

代码:

@Input() group; 
@Input() control; 
@Input() element; 

模板:

<div [formGroup]="group"> 
    <input 
    type="text" 
    [placeholder]="element?.description?.placeholder?.text" 
    [value]="control?.value" 
    [formControl]="control"> 
</div> 
+0

我明确表示我想避免通过@Inputs传递所有内容,因为我必须处理更多级别的组成不止1个。 – Celebes