私は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;
私はコードに何かが正しくないことを知っていますが、私は本当に障害になっています。どんな助けも認められます。
ありがとう。
主なアイデアは、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;
}
}
子コンポーネントに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>
嘘をつくつもりはありません。以前この記事を見つけられなかった理由がわかりません。
解決策は、入力として親から子にformGroupを渡すことにより、子コンポーネントを同じformGroupにバインドすることです。
誰かが他の方法で問題を解決するためのコードを共有する場合、喜んで受け入れます。
親フォームへの参照を取得するには、単にこれを使用します(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" />
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">
_