带有子组件和验证的 Angular 2 嵌套表单 [英] Angular 2 nested forms with child components and validation

查看:20
本文介绍了带有子组件和验证的 Angular 2 嵌套表单的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试在 Angular 2 中实现一个带有验证的嵌套表单,我看过帖子并遵循了文档,但我真的很挣扎,希望你能指出我正确的方向.

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

我想要完成什么

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

<div [formGroup]="userForm" novalidate>

<label>用户 ID</label><输入formControlName="userId"></div>

<label>虚拟</label><输入表单ControlName="dummyInput"></div></div>

这需要一个这样的类:

私人用户表单:FormGroup;构造函数(私人FB:FormBuilder){this.createForm();}私有 createForm(): 无效{this.userForm = this.fb.group({userId: ["", Validators.required],dummyInput: "", Validators.required]});}

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

parent.component.html

<div [formGroup]="userForm" novalidate>

<label>用户 ID</label><输入formControlName="userId"></div>

<dummy></dummy></div></div>

parent.component.ts

private createForm(): void{this.userForm = this.fb.group({userId: ["", Validators.required],假人:this.fb.group({dummyInput: ["", Validators.required]})});

children.component.html

<div [formGroup]="dummyGroup"><label>虚拟输入:</label><输入表单ControlName="dummyInput"></div>

children.component.ts

private dummyGroup: FormGroup;

我知道代码有问题,但我确实遇到了障碍.任何帮助将不胜感激.

谢谢.

解决方案

主要思想就是要把formGroup和formControls当作变量,主要是javascript对象和数组.

所以我会输入一些代码来说明我的观点.下面的代码有点像你所拥有的.该表单是动态构建的,只是它被分成多个部分,每个部分包含其共享的字段和标签.

HTML 由 typescript 类支持.那些不在这里,因为它们没有什么特别之处.只有 FormSchemaUI、FormSectionUI 和 FormFieldUI 很重要.

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

另外请注意,formSchema: FormSchema 是我从服务接收的 JSON 对象.您没有看到定义的 UI 类的任何属性都是从它们的基本 Data 类继承的.这些不在此处介绍.层次结构是: FormSchema 包含多个部分.一个部分包含多个字段.

<form (ngSubmit)="onSubmit()" #ciRegisterForm="ngForm" [formGroup]="formSchemaUI.MainFormGroup"><按钮 kendoButton (click)="onSubmit(ciRegisterForm)" [disabled]="!canSubmit()">注册 {{registerPageName}} </button><br/><br/><app-ci-register-section *ngFor="让formSchemaUI.SectionsUI的sectionUI" [sectionUI]="sectionUI"></app-ci-register-section><按钮 kendoButton (click)="onSubmit(ciRegisterForm)" [disabled]="!canSubmit()">注册 {{registerPageName}} </button></表格>

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

<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='让sectionUI.FieldsUI的fieldUI;让我=索引;让偶数 = 偶数;'><div class="row" *ngIf="even"><ng-容器><div class="col-md-6 col-lg-6" app-ci-field-label-tuple [fieldUI]="fieldUI"></div></ng-容器><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-容器></div></ng-容器></字段集></div></div>

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

{{fieldUI.Label}}

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

<ng-container><div 类="行"><div class="col-md-4 col-lg-4 text-right"><标签为="{{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-容器>

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

<ng-container [formGroup]="fieldUI.ParentSectionFormGroup"><ng-container *ngIf="fieldUI.isEnabled"><ng-container [ngSwitch]="fieldUI.ColumnType"><输入 *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-容器></ng-容器></ng-容器>

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

导出类 FormSchemaUI 扩展 FormSchema {SectionsUI: 数组;MainFormGroup:FormGroup;静态 fromFormSchemaData(formSchema: FormSchema): FormSchemaUI {让 formSchemaUI = new FormSchemaUI(formSchema);formSchemaUI.SectionsUI = 新数组();formSchemaUI.Sections.forEach(section => {让 formSectionUI = FormSectionUI.fromFormSectionData(section);formSchemaUI.SectionsUI.push(formSectionUI);});formSchemaUI.MainFormGroup = FormSchemaUI.buildMainFormGroup(formSchemaUI);返回表单SchemaUI;}静态 buildMainFormGroup(formSchemaUI: FormSchemaUI): FormGroup {让 obj = {};formSchemaUI.SectionsUI.forEach(sectionUI => {obj[sectionUI.SectionDisplayId] = sectionUI.SectionFormGroup;});让 sectionFormGroup = new FormGroup(obj);返回sectionFormGroup;}}

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

导出类 FormSectionUI 扩展 FormSection {构造函数(formSection:FormSection){this.SectionDisplayId = 'section' + this.SectionId.toString();}SectionDisplayId:字符串;FieldsUI: 数组;HiddenFieldsUI: Array;SectionFormGroup:FormGroup;MainFormGroup:FormGroup;ParentFormSchemaUI:FormSchemaUI;静态 fromFormSectionData(formSection: FormSection): FormSectionUI {让 formSectionUI = new FormSectionUI(formSection);formSectionUI.FieldsUI = 新数组();formSectionUI.HiddenFieldsUI = 新数组();formSectionUI.Fields.forEach(field => {让 fieldUI = FormFieldUI.fromFormFieldData(field);if (fieldUI.ColumnType != 'HIDDEN')formSectionUI.FieldsUI.push(fieldUI);否则 formSectionUI.HiddenFieldsUI.push(fieldUI);});formSectionUI.SectionFormGroup = FormSectionUI.buildFormSectionFormGroup(formSectionUI);返回表单SectionUI;}静态 buildFormSectionFormGroup(formSectionUI: FormSectionUI): FormGroup {让 obj = {};formSectionUI.FieldsUI.forEach(fieldUI => {obj[fieldUI.FieldDisplayId] = fieldUI.FieldFormControl;});让 sectionFormGroup = new FormGroup(obj);返回sectionFormGroup;}}

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

导出类 FormFieldUI 扩展 FormField {构造函数(formField:FormField){极好的();this.FieldDisplayId = 'field' + this.FieldId.toString();this.ListItems = new Array();}公共字段显示ID:字符串;公共字段表单控件:表单控件;公共ParentSectionFormGroup:FormGroup;公共 MainFormGroup:FormGroup;公共ParentFormSectionUI:FormSectionUI;public ValueChange: EventEmitter;= new EventEmitter<any>();静态 buildFormControl(formFieldUI:FormFieldUI): FormControl {让 nullValidator = Validators.nullValidator;让 fieldKey: string = formFieldUI.FieldDisplayId;让字段值:任何;开关(formFieldUI.ColumnType){默认:fieldValue = formFieldUI.Value;休息;}让 isDisabled = !formFieldUI.IsEnabled;let validatorsArray: ValidatorFn[] = new Array();让 asyncValidatorsArray: AsyncValidatorFn[] = new Array();让 formControl = new FormControl({ value: fieldValue, disabled: isDisabled }, validatorsArray, asyncValidatorsArray);返回表单控件;}}

I'm trying achieve a nested form with validation in Angular 2, I've seen posts and followed the documentation but I'm really struggling, hope you can point me in the right direction.

What I am trying to achieve is having a validated form with multiple children components. These children components are a bit complex, some of them have more children components, but for the sake of the question I think we can attack the problem having a parent and a children.

What am I trying to accomplish

Having a form that works like this:

<div [formGroup]="userForm" novalidate>
    <div>
        <label>User Id</label>
        <input formControlName="userId">
    </div>
    <div>
        <label>Dummy</label>
        <input formControlName="dummyInput">
    </div>
</div>

This requires having a class like this:

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

This works as expected, but now I want to decouple the code, and put the "dummyInput" functionality in a separate, different component. This is where I get lost. This is what I tried, I think I'm not far from getting the answer, but I'm really out of ideas, I'm fairly new to the scene:

parent.component.html

<div [formGroup]="userForm" novalidate>
    <div>
        <label>User Id</label>
        <input formControlName="userId">
    </div>
    <div>
        <dummy></dummy>
    </div>
</div>

parent.component.ts

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

children.component.html

<div [formGroup]="dummyGroup">
    <label>Dummy Input: </label>
    <input formControlName="dummyInput">
</div>

children.component.ts

private dummyGroup: FormGroup;

I know something is not right with the code, but I'm really in a roadblock. Any help would be aprreciated.

Thanks.

解决方案

The main idea is that you have to treat the formGroup and formControls as variables, mainly javascript objects and arrays.

So I'll put some code in to make my point. The code below is somewhat like what you have. The form is constructed dynamically, just that it is split into sections, each section containing its share of fields and labels.

The HTML is backed up by typescript classes. Those are not here as they do not have much special. Just the FormSchemaUI, FormSectionUI and FormFieldUI are important.

Treat each piece of code as its own file.

Also please take note that formSchema: FormSchema is a JSON object that I receive from a service. Any properties of the UI classes that you do not see defined are inherited from their base Data clases. Those are not presented here. The hierarchy is: FormSchema contains multiple sections. A section contains multiple fields.

<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>

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

<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>

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

{{fieldUI.Label}}

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

<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>

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

<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>

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

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;
    }
}

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

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;
    }
}

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

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;
    }
}

这篇关于带有子组件和验证的 Angular 2 嵌套表单的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆