Angular 2嵌套表单,包含子组件和验证

Mes*_*ese 14 forms validation components angular

我正在尝试使用Angular 2中的验证来实现嵌套表单,我已经看到了帖子并且遵循了文档,但我真的很挣扎,希望你能指出我正确的方向.

我想要实现的是拥有一个带有多个子组件的经过验证的表单.这些子组件有点复杂,其中一些有更多的子组件,但为了这个问题,我认为我们可以解决有父母和孩子的问题.

我想要完成什么?

有一个像这样工作的表单:

<div [formGroup]="userForm" novalidate>
    <div>
        <label>User Id</label>
        <input formControlName="userId">
    </div>
    <div>
        <label>Dummy</label>
        <input formControlName="dummyInput">
    </div>
</div>
Run Code Online (Sandbox Code Playgroud)

这需要一个像这样的类:

private userForm: FormGroup;
constructor(private fb: FormBuilder){
    this.createForm();
}
private createForm(): void{
    this.userForm = this.fb.group({
        userId: ["", Validators.required],
        dummyInput: "", Validators.required]
    });
}
Run Code Online (Sandbox Code Playgroud)

这按预期工作,但现在我想要解耦代码,并将"dummyInput"功能放在一个单独的不同组件中.这是我迷路的地方.这就是我的尝试,我想我离答案不远,但我真的没有想法,我对这个场景很新:

parent.component.html

<div [formGroup]="userForm" novalidate>
    <div>
        <label>User Id</label>
        <input formControlName="userId">
    </div>
    <div>
        <dummy></dummy>
    </div>
</div>
Run Code Online (Sandbox Code Playgroud)

parent.component.ts

private createForm(): void{
    this.userForm = this.fb.group({
    userId: ["", Validators.required],
    dummy: this.fb.group({
        dummyInput: ["", Validators.required]
    })
});
Run Code Online (Sandbox Code Playgroud)

children.component.html

<div [formGroup]="dummyGroup">
    <label>Dummy Input: </label>
    <input formControlName="dummyInput">
</div>
Run Code Online (Sandbox Code Playgroud)

children.component.ts

private dummyGroup: FormGroup;
Run Code Online (Sandbox Code Playgroud)

我知道代码有些不对劲,但我真的遇到了障碍.任何帮助都会受到重视.

谢谢.

Moh*_*HID 10

您可以在子组件中添加一个Input以将FormGroup传递给它.并使用​​FormGroupName传递FormGroup的名称:)

children.component.ts

@Input('group');
private dummyGroup: FormGroup;
Run Code Online (Sandbox Code Playgroud)

parent.component.html

<div [formGroup]="userForm" novalidate>
    <div>
        <label>User Id</label>
        <input formControlName="userId">
    </div>
    <div formGroupName="dummy">
        <dummy [group]="userForm.controls['dummy']"></dummy>
    </div>
</div>
Run Code Online (Sandbox Code Playgroud)

  • 这部分: [group]="userForm.controls['dummy']" 是解决方案所在。我花了一天时间试图找到此问题的解决方案,将组传递给子组件。我有一个 ngFor。T 恤最终代码如下所示: [group]="formGroup.controls['section'+section.SectionId]" (2认同)

Dra*_*lut 1

主要思想是你必须将 formGroup 和 formControls 视为变量,主要是 javascript 对象和数组。

因此,我将添加一些代码来阐明我的观点。下面的代码有点像你所拥有的。该表单是动态构建的,只是它被分成几个部分,每个部分包含其共享的字段和标签。

HTML 由打字稿类支持。这些不在这里,因为它们没有太多特别之处。只有 FormSchemaUI、FormSectionUI 和 FormFieldUI 很重要。

将每段代码视为自己的文件。

另请注意formSchema:FormSchema是我从服务收到的 JSON 对象。您未看到定义的 UI 类的任何属性都是从其基数据类继承的。这些不在此介绍。层次结构是: FormSchema 包含多个部分。一个部分包含多个字段。

<form (ngSubmit)="onSubmit()" #ciRegisterForm="ngForm" [formGroup]="formSchemaUI.MainFormGroup">
    <button kendoButton (click)="onSubmit(ciRegisterForm)" [disabled]="!canSubmit()"> Register {{registerPageName}} </button>
    <br /><br />
    <app-ci-register-section *ngFor="let sectionUI of formSchemaUI.SectionsUI" [sectionUI]="sectionUI">
    </app-ci-register-section>
    <button kendoButton (click)="onSubmit(ciRegisterForm)" [disabled]="!canSubmit()"> Register {{registerPageName}} </button>
</form>
Run Code Online (Sandbox Code Playgroud)

===============================================

<div class="row" [formGroup]="sectionUI.MainFormGroup">
    <div class="col-md-12  col-lg-12" [formGroupName]="sectionUI.SectionDisplayId">
        <fieldset class="section-border">
            <legend class="section-border">{{sectionUI.Title}}</legend>
            <ng-container *ngFor='let fieldUI of sectionUI.FieldsUI; let i=index; let even = even;'>
                <div class="row" *ngIf="even">
                    <ng-container>
                        <div class="col-md-6  col-lg-6" app-ci-field-label-tuple [fieldUI]="fieldUI">

                        </div>
                    </ng-container>
                    <ng-container *ngIf="sectionUI.Fields[i+1]">
                        <div class="col-md-6  col-lg-6" app-ci-field-label-tuple [fieldUI]="sectionUI.FieldsUI[i+1]">

                        </div>
                    </ng-container>
                </div>
            </ng-container>           
        </fieldset>
    </div>
</div>
Run Code Online (Sandbox Code Playgroud)

===============================================

{{fieldUI.标签}}

===============================================

<ng-container>
    <div class="row">
        <div class="col-md-4 col-lg-4 text-right">
            <label for="{{fieldUI.FieldDisplayId}}"> {{fieldUI.Label}} </label>
        </div>
        <div class="col-md-8 col-lg-8">
            <div app-ci-field-edit [fieldUI]="fieldUI" ></div>
        </div>
    </div>       
</ng-container>
Run Code Online (Sandbox Code Playgroud)

===============================================

<ng-container [formGroup]="fieldUI.ParentSectionFormGroup">    
    <ng-container *ngIf="fieldUI.isEnabled">         
        <ng-container [ngSwitch]="fieldUI.ColumnType">            
            <input *ngSwitchCase="'HIDDEN'" type="hidden" id="{{fieldUI.FieldDisplayId}}" [value]="fieldUI.Value" />
            <ci-field-textbox *ngSwitchDefault
                              [fieldUI]="fieldUI"
                              (valueChange)="onValueChange($event)"
                              class="fullWidth" style="width:100%">
            </ci-field-textbox>
        </ng-container>       
    </ng-container>
</ng-container>
Run Code Online (Sandbox Code Playgroud)

===============================================

export class FormSchemaUI extends FormSchema { 

    SectionsUI: Array<FormSectionUI>;
    MainFormGroup: FormGroup;

    static fromFormSchemaData(formSchema: FormSchema): FormSchemaUI {
        let formSchemaUI = new FormSchemaUI(formSchema);
        formSchemaUI.SectionsUI = new Array<FormSectionUI>();
        formSchemaUI.Sections.forEach(section => {
            let formSectionUI = FormSectionUI.fromFormSectionData(section);

            formSchemaUI.SectionsUI.push(formSectionUI);
        });
        formSchemaUI.MainFormGroup = FormSchemaUI.buildMainFormGroup(formSchemaUI);        
        return formSchemaUI;
    }

    static buildMainFormGroup(formSchemaUI: FormSchemaUI): FormGroup {
        let obj = {};
        formSchemaUI.SectionsUI.forEach(sectionUI => {
            obj[sectionUI.SectionDisplayId] = sectionUI.SectionFormGroup;
        });
        let sectionFormGroup = new FormGroup(obj);
        return sectionFormGroup;
    }
}
Run Code Online (Sandbox Code Playgroud)

===============================================

export class FormSectionUI extends FormSection {

    constructor(formSection: FormSection) {        
        this.SectionDisplayId = 'section' + this.SectionId.toString();
    }

    SectionDisplayId: string;
    FieldsUI: Array<FormFieldUI>;
    HiddenFieldsUI: Array<FormFieldUI>;
    SectionFormGroup: FormGroup;
    MainFormGroup: FormGroup;
    ParentFormSchemaUI: FormSchemaUI;

    static fromFormSectionData(formSection: FormSection): FormSectionUI {
        let formSectionUI = new FormSectionUI(formSection);
        formSectionUI.FieldsUI = new Array<FormFieldUI>();
        formSectionUI.HiddenFieldsUI = new Array<FormFieldUI>();
        formSectionUI.Fields.forEach(field => {
            let fieldUI = FormFieldUI.fromFormFieldData(field);
            if (fieldUI.ColumnType != 'HIDDEN')
                formSectionUI.FieldsUI.push(fieldUI);
            else formSectionUI.HiddenFieldsUI.push(fieldUI);
        });
        formSectionUI.SectionFormGroup = FormSectionUI.buildFormSectionFormGroup(formSectionUI);
        return formSectionUI;
    }

    static buildFormSectionFormGroup(formSectionUI: FormSectionUI): FormGroup {
        let obj = {};
        formSectionUI.FieldsUI.forEach(fieldUI => {
            obj[fieldUI.FieldDisplayId] = fieldUI.FieldFormControl;
        });
        let sectionFormGroup = new FormGroup(obj);
        return sectionFormGroup;
    }
}
Run Code Online (Sandbox Code Playgroud)

===============================================

export class FormFieldUI extends FormField {    

    constructor(formField: FormField) {
    super();
    this.FieldDisplayId = 'field' + this.FieldId.toString();       

    this.ListItems = new Array<SelectListItem>();        
   }

    public FieldDisplayId: string;

    public FieldFormControl: FormControl;
    public ParentSectionFormGroup: FormGroup;
    public MainFormGroup: FormGroup;
    public ParentFormSectionUI: FormSectionUI;  

    public ValueChange: EventEmitter<any> = new EventEmitter<any>();    

    static buildFormControl(formFieldUI:FormFieldUI): FormControl {
        let nullValidator = Validators.nullValidator;

        let fieldKey: string = formFieldUI.FieldDisplayId; 
        let fieldValue: any;
        switch (formFieldUI.ColumnType) {            
            default:
                fieldValue = formFieldUI.Value;
                break;
        }
        let isDisabled = !formFieldUI.IsEnabled;
        let validatorsArray: ValidatorFn[] = new Array<ValidatorFn>();
        let asyncValidatorsArray: AsyncValidatorFn[] = new Array<AsyncValidatorFn>();

        let formControl = new FormControl({ value: fieldValue, disabled: isDisabled }, validatorsArray, asyncValidatorsArray);
        return formControl;
    }
}
Run Code Online (Sandbox Code Playgroud)