Angular 4 具有嵌套组的动态表单
Angular 4 Dynamic form with nested groups
我想从树结构生成反应形式。
下面是创建表单项(表单组和控件)的代码。对于嵌套在表单组中的控件,我使用递归模板。
import { Component, Input, NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { FormBuilder, FormGroup, FormsModule, ReactiveFormsModule, Validators } from '@angular/forms';
@Component({
selector: 'form-item',
template: `
<ng-container [formGroup]="form">
<ng-container *ngTemplateOutlet="formItemTemplate; context:{ $implicit: pathSegments }"></ng-container>
<ng-template #formItemTemplate let-pathSegments>
<ng-container *ngIf="pathSegments.length > 1">
<div [formGroupName]="pathSegments[0]" class="form-group">
<ng-container *ngTemplateOutlet="formItemTemplate; context:{ $implicit: pathSegments.slice(1) }"></ng-container>
</div>
</ng-container>
<ng-container *ngIf="pathSegments.length === 1">
<div class="form-control">
<label>{{pathSegments[pathSegments.length - 1]}}</label>
<!--<input type="text" [formControlName]="pathSegments[pathSegments.length - 1]"/>-->
<input type="text"/>
</div>
</ng-container>
</ng-template>
</ng-container>
`,
})
export class AppControlComponent {
@Input() form: FormGroup;
@Input()
set path(path: string) {
this.pathSegments = path.split('.');
}
pathSegments: string[] = [];
constructor() { }
}
@Component({
selector: 'app-root',
template: `
<form [formGroup]="questionForm">
<h1>Question Form</h1>
<form-item [form]="questionForm" path="question"></form-item>
<form-item [form]="questionForm" path="answers.answer1"></form-item>
<form-item [form]="questionForm" path="answers.answer2"></form-item>
<form-item [form]="questionForm" path="answers.answer3"></form-item>
<button type="submit">submit</button>
<pre>{{questionForm.value | json}}</pre>
</form>
`,
})
export class AppComponent {
private questionForm: FormGroup;
constructor(private fb: FormBuilder) {
this.questionForm = this.fb.group({
question: ['', Validators.required],
answers: this.fb.group({
answer1: ['', Validators.required],
answer2: ['', Validators.required],
answer3: ['', Validators.required],
}),
});
}
}
@NgModule({
imports: [BrowserModule, FormsModule, ReactiveFormsModule],
declarations: [
AppComponent,
AppControlComponent
],
bootstrap: [AppComponent]
})
export class AppModule {}
表格HTML完美生成。但是,当我将 formControlName
指令添加到输入时,HTML 生成中断。然后我得到这样的错误:
Error: Cannot find control with name: 'answer1'
正如@yurzui 在评论中提到的,出现问题是因为 FormControlName
指令使用
@Optional() @Host() @SkipSelf() parent: ControlContainer
找到父 formGroup。
因此,以下代码工作正常,但包含代码重复并且支持有限数量的嵌套组。
@Component({
selector: 'form-item',
template: `
<ng-container [formGroup]="form">
<div class="form-control" *ngIf="pathSegments.length === 1">
<label>{{pathSegments[pathSegments.length - 1]}}</label>
<input type="text" [formControlName]="pathSegments[pathSegments.length - 1]"/>
</div>
<ng-container *ngIf="pathSegments.length === 2" [formGroupName]="pathSegments[0]">
<div class="form-control">
<label>{{pathSegments[pathSegments.length - 1]}}</label>
<input type="text" [formControlName]="pathSegments[pathSegments.length - 1]"/>
</div>
</ng-container>
<ng-container *ngIf="pathSegments.length === 3" [formGroupName]="pathSegments[0]">
<ng-container [formGroupName]="pathSegments[1]">
<div class="form-control">
<label>{{pathSegments[pathSegments.length - 1]}}</label>
<input type="text" [formControlName]="pathSegments[pathSegments.length - 1]"/>
</div>
</ng-container>
</ng-container>
<ng-container *ngIf="pathSegments.length === 4" [formGroupName]="pathSegments[0]">
<ng-container [formGroupName]="pathSegments[1]">
<ng-container [formGroupName]="pathSegments[2]">
<div class="form-control">
<label>{{pathSegments[pathSegments.length - 1]}}</label>
<input type="text" [formControlName]="pathSegments[pathSegments.length - 1]"/>
</div>
</ng-container>
</ng-container>
</ng-container>
</ng-container>
`,
})
有什么改进方法吗?
为了支持无限数量的嵌套组,我会使用如下标记:
<ng-container [formGroup]="form">
<ng-container
*ngTemplateOutlet="formItemTemplate; context:{ $implicit: 0, group: form }">
</ng-container>
<ng-template #formItemTemplate let-i let-group="group">
<fieldset *ngIf="i < pathSegments.length - 1" class="form-group">
<legend>{{ pathSegments[i] }}</legend>
<ng-container
*ngTemplateOutlet="formItemTemplate;
context:{ $implicit: i + 1, group: group.get(pathSegments[i]) }">
</ng-container>
</fieldset>
<div *ngIf="i + 1 === pathSegments.length" class="form-control" [formGroup]="group">
<label>{{pathSegments[pathSegments.length - 1]}}</label>
<input [formControlName]="pathSegments[pathSegments.length - 1]"/>
</div>
</ng-template>
</ng-container>
我想从树结构生成反应形式。
下面是创建表单项(表单组和控件)的代码。对于嵌套在表单组中的控件,我使用递归模板。
import { Component, Input, NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { FormBuilder, FormGroup, FormsModule, ReactiveFormsModule, Validators } from '@angular/forms';
@Component({
selector: 'form-item',
template: `
<ng-container [formGroup]="form">
<ng-container *ngTemplateOutlet="formItemTemplate; context:{ $implicit: pathSegments }"></ng-container>
<ng-template #formItemTemplate let-pathSegments>
<ng-container *ngIf="pathSegments.length > 1">
<div [formGroupName]="pathSegments[0]" class="form-group">
<ng-container *ngTemplateOutlet="formItemTemplate; context:{ $implicit: pathSegments.slice(1) }"></ng-container>
</div>
</ng-container>
<ng-container *ngIf="pathSegments.length === 1">
<div class="form-control">
<label>{{pathSegments[pathSegments.length - 1]}}</label>
<!--<input type="text" [formControlName]="pathSegments[pathSegments.length - 1]"/>-->
<input type="text"/>
</div>
</ng-container>
</ng-template>
</ng-container>
`,
})
export class AppControlComponent {
@Input() form: FormGroup;
@Input()
set path(path: string) {
this.pathSegments = path.split('.');
}
pathSegments: string[] = [];
constructor() { }
}
@Component({
selector: 'app-root',
template: `
<form [formGroup]="questionForm">
<h1>Question Form</h1>
<form-item [form]="questionForm" path="question"></form-item>
<form-item [form]="questionForm" path="answers.answer1"></form-item>
<form-item [form]="questionForm" path="answers.answer2"></form-item>
<form-item [form]="questionForm" path="answers.answer3"></form-item>
<button type="submit">submit</button>
<pre>{{questionForm.value | json}}</pre>
</form>
`,
})
export class AppComponent {
private questionForm: FormGroup;
constructor(private fb: FormBuilder) {
this.questionForm = this.fb.group({
question: ['', Validators.required],
answers: this.fb.group({
answer1: ['', Validators.required],
answer2: ['', Validators.required],
answer3: ['', Validators.required],
}),
});
}
}
@NgModule({
imports: [BrowserModule, FormsModule, ReactiveFormsModule],
declarations: [
AppComponent,
AppControlComponent
],
bootstrap: [AppComponent]
})
export class AppModule {}
表格HTML完美生成。但是,当我将 formControlName
指令添加到输入时,HTML 生成中断。然后我得到这样的错误:
Error: Cannot find control with name: 'answer1'
正如@yurzui 在评论中提到的,出现问题是因为 FormControlName
指令使用
@Optional() @Host() @SkipSelf() parent: ControlContainer
找到父 formGroup。
因此,以下代码工作正常,但包含代码重复并且支持有限数量的嵌套组。
@Component({
selector: 'form-item',
template: `
<ng-container [formGroup]="form">
<div class="form-control" *ngIf="pathSegments.length === 1">
<label>{{pathSegments[pathSegments.length - 1]}}</label>
<input type="text" [formControlName]="pathSegments[pathSegments.length - 1]"/>
</div>
<ng-container *ngIf="pathSegments.length === 2" [formGroupName]="pathSegments[0]">
<div class="form-control">
<label>{{pathSegments[pathSegments.length - 1]}}</label>
<input type="text" [formControlName]="pathSegments[pathSegments.length - 1]"/>
</div>
</ng-container>
<ng-container *ngIf="pathSegments.length === 3" [formGroupName]="pathSegments[0]">
<ng-container [formGroupName]="pathSegments[1]">
<div class="form-control">
<label>{{pathSegments[pathSegments.length - 1]}}</label>
<input type="text" [formControlName]="pathSegments[pathSegments.length - 1]"/>
</div>
</ng-container>
</ng-container>
<ng-container *ngIf="pathSegments.length === 4" [formGroupName]="pathSegments[0]">
<ng-container [formGroupName]="pathSegments[1]">
<ng-container [formGroupName]="pathSegments[2]">
<div class="form-control">
<label>{{pathSegments[pathSegments.length - 1]}}</label>
<input type="text" [formControlName]="pathSegments[pathSegments.length - 1]"/>
</div>
</ng-container>
</ng-container>
</ng-container>
</ng-container>
`,
})
有什么改进方法吗?
为了支持无限数量的嵌套组,我会使用如下标记:
<ng-container [formGroup]="form">
<ng-container
*ngTemplateOutlet="formItemTemplate; context:{ $implicit: 0, group: form }">
</ng-container>
<ng-template #formItemTemplate let-i let-group="group">
<fieldset *ngIf="i < pathSegments.length - 1" class="form-group">
<legend>{{ pathSegments[i] }}</legend>
<ng-container
*ngTemplateOutlet="formItemTemplate;
context:{ $implicit: i + 1, group: group.get(pathSegments[i]) }">
</ng-container>
</fieldset>
<div *ngIf="i + 1 === pathSegments.length" class="form-control" [formGroup]="group">
<label>{{pathSegments[pathSegments.length - 1]}}</label>
<input [formControlName]="pathSegments[pathSegments.length - 1]"/>
</div>
</ng-template>
</ng-container>