web-dev-qa-db-ja.com

Angular 2つの子コンポーネントと検証を含むネストされたフォーム

私はAngular 2の検証でネストされたフォームを達成しようとしています。

私が達成しようとしているのは、複数の子コンポーネントを持つ検証済みフォームを持つことです。これらの子コンポーネントは少し複雑で、一部にはより多くの子コンポーネントがありますが、質問のために、親と子を持つ問題を攻撃できると思います。

私が達成しようとしていること

このように動作するフォームを持っている:

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

これには、次のようなクラスが必要です。

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

これは期待通りに機能しますが、コードを分離し、「dummyInput」機能を別の異なるコンポーネントに配置したいと思います。これは私が迷子になる場所です。これは私が試したものです、私は答えを得るのにそれほど遠くないと思いますが、私は本当にアイデアから外れています、私はシーンにかなり新しいです:

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;

私はコードに何かが正しくないことを知っていますが、私は本当に障害になっています。どんな助けも認められます。

ありがとう。

13
Mese

主なアイデアは、formGroupとformControlsを変数、主にjavascriptオブジェクトと配列として扱う必要があるということです。

だから、私は私のポイントを作るためにいくつかのコードを入れます。以下のコードは、あなたが持っているものにいくらか似ています。フォームは、セクションに分割され、各セクションにフィールドとラベルの共有が含まれるだけで、動的に構築されます。

HTMLは、TypeScriptクラスによってバックアップされます。それらはあまり特別なものがないので、ここにはありません。 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>

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

<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;
    }
}
1
Dragos Durlut

子コンポーネントにInputを追加してFormGroupを渡すことができ、FormGroupNameを使用してFormGroupの名前を渡すことができます:)

children.component.ts

@Input('group');
private dummyGroup: FormGroup;

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

嘘をつくつもりはありません。以前この記事を見つけられなかった理由がわかりません。

角度2:子コンポーネントを含むフォーム

解決策は、入力として親から子にformGroupを渡すことにより、子コンポーネントを同じformGroupにバインドすることです。

誰かが他の方法で問題を解決するためのコードを共有する場合、喜んで受け入れます。

3
Mese

親フォームへの参照を取得するには、単にこれを使用します(Angular 2.では利用できない可能性があります。2. Angular 6)でテストしました:

[〜#〜] ts [〜#〜]

import {
   FormGroup,
   ControlContainer,
   FormGroupDirective,
} from "@angular/forms";

@Component({
  selector: "app-leveltwo",
  templateUrl: "./leveltwo.component.html",
  styleUrls: ["./leveltwo.component.sass"],
  viewProviders: [
    {
      provide: ControlContainer,
      useExisting: FormGroupDirective
    }
  ]
})
export class NestedLevelComponent implements OnInit {
  //form: FormGroup;

  constructor(private parent: FormGroupDirective) {
     //this.form = form;
  }
}

[〜#〜] html [〜#〜]

<input type="text" formControlName="test" />
1
blacksheep_2011
import { Directive } from '@angular/core';
import { ControlContainer, NgForm } from '../../../node_modules/@angular/forms';

@Directive({
  selector: '[ParentProvider]',
  providers: [
    {
    provide: ControlContainer,
    useFactory: function (form: NgForm) {
      return form;
    },
    deps: [NgForm]
    }`enter code here`
  ]
})
export class ParentProviderDirective {

  constructor() { }

}
<div ParentProvider >
  for child
</div>

FormGroupDirective@ blacksheep's answer で説明されている)の代わりに、ControlContainerを使用します。

_import { FormGroup, ControlContainer } from "@angular/forms";

export class ChildComponent implements OnInit {

  formGroup: FormGroup;

  constructor(private controlContainer: ControlContainer) {}

  ngOnInit() {
    this.formGroup = <FormGroup>this.controlContainer.control;
  }
_

formGroupは、直接の親またはさらに上位(親の親など)に設定できます。これにより、formGroupを渡すために@Input() sのチェーンを必要とせずに、さまざまなネストされたコンポーネントにfromグループを渡すことができます。親でformGroupを設定して、子のControlContainerを介して利用できるようにします。

_<... [formGroup]="myFormGroup">
_
0
Markus Pscheidt