2016-07-28 54 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