首页 > 嗟来之食 > [Angular2] Dynamic Forms(动态表单) – (none) –
2016
07-29

[Angular2] Dynamic Forms(动态表单) – (none) –

我们可以使用NgFormModel动态渲染表单。
接下来我们使用动态表单来构建一个调查问卷的应用。
首先定义一个可以描述表单所有场景的对象模型,调查问卷表单中包含很多问题,这个问题就是模型中最基础的对象。
创建QuestionBase类:
// app/question-base.ts

export class QuestionBase<T>{
value:T;
key:string;
label:string;
required:boolean;
order:number;
controlType:string;

constructor(options:{
value?:T,
key?:string,
label?:string,
required?:boolean,
order?:number,
controlType?:string
} = {}){
this.value = options.value;
this.key = options.key || '';
this.label = options.label || '';
this.required = !!options.required;
this.order = options.order === undefined ? 1 : options.order;
this.controlType = options.controlType || '';
}
}
从QuestionBase继承出两个类TextboxQuestion和DropdownQuestion,分别用于文本框问题和下拉选择框问题。
TextboxQuestion通过type属性来支持html5类型,如text,email,url等等。
// app/question-textbox.ts

import {QuestionBase} from './question-base';

export class TextboxQuestion extends QuestionBase<string>{
controlType = 'textbox';
type:string;

constructor(options:{} = {}){
super(options);
this.type = options['type'] || '';
}
}
DropdownQuestion如下:
// app/question-dropdown.ts

import {QuestionBase} from './question-base';

export class DropdownQuestion extends QuestionBase<string>{
controlType = 'dropdown';
options:{key:string, value:string}[] = [];

constructor(options:{} = {}){
super(options);
this.options = options['options'] || [];
}
}
接下来定义一个QuestionControlService,作用时将问题转换成ngForm控件组。控件组从问卷模型中获取元数据,并且允许我们指定默认值和验证规则。
// app/question-control.service.ts

import {Injectable} from 'angular2/core';
import {ControlGroup, FormBuilder, Validators} from 'angular2/common';
import {QuestionBase} from './question-base';

@Injectable()
export class QuestionControlService {
constructor(private _fb:FormBuilder){ }

toControlGroup(questions:QuestionBase<any>[] ) {
let group = {};

questions.forEach(question => {
group[question.key] = question.required ? [question.value || '', Validators.required] : [];
});
return this._fb.group(group);
}
}
我们已经定义好了完整的模型,接下来可以创建渲染动态表单的组件了。
// app/dynamic-form.component.html

<div>
<form (ngSubmit)="onSubmit()" [ngFormModel]="form">

<div *ngFor="#question of questions" class="form-row">
<df-question [question]="question" [form]="form"></df-question>
</div>

<div class="form-row">
<button type="submit" [disabled]="!form.valid">Save</button>
</div>
</form>

<div *ngIf="payLoad" class="form-row">
<strong>Saved the following values</strong><br>{{payLoad}}
</div>
</div>
// app/dynamic-form.component.ts

import {Component, Input, OnInit} from 'angular2/core';
import {ControlGroup} from 'angular2/common';

import {QuestionBase} from './question-base';
import {QuestionControlService} from './question-control.service';
import {DynamicFormQuestionComponent} from './dynamic-form-question.component';

@Component({
selector:'dynamic-form',
templateUrl:'app/dynamic-form.component.html',
directives: [DynamicFormQuestionComponent],
providers: [QuestionControlService]
})
export class DynamicForm {
@Input() questions: QuestionBase<any>[] = [];
form: ControlGroup;
payLoad = '';

constructor(private _qcs: QuestionControlService) { }

ngOnInit(){
this.form = this._qcs.toControlGroup(this.questions);
}

onSubmit() {
this.payLoad = JSON.stringify(this.form.value);
}
}
它将展示问题列表,每个问题绑定到<df-question>组件元素。<df-question>标签对应的是组件DynamicFormQuestionComponent,作用是根据绑定的问题对象渲染每一个的问题:
// app/dynamic-form-question.component.html

<div [ngFormModel]="form">
<label [attr.for]="question.key">{{question.label}}</label>

<div [ngSwitch]="question.controlType">

<input *ngSwitchWhen="'textbox'" [ngControl]="question.key"
[id]="question.key" [type]="question.type">

<select [id]="question.key" *ngSwitchWhen="'dropdown'" [ngControl]="question.key">
<option *ngFor="#opt of question.options" [value]="opt.key">{{opt.value}}</option>
</select>

</div>

<div class="errorMessage" *ngIf="!isValid">{{question.label}} is required</div>
</div>
// app/dynamic-form-question.component.ts

import {Component, Input} from 'angular2/core';
import {ControlGroup} from 'angular2/common';
import {QuestionBase} from './question-base';

@Component({
selector:'df-question',
templateUrl:'app/dynamic-form-question.component.html'
})
export class DynamicFormQuestionComponent {
@Input() question:QuestionBase<any>;
@Input() form:ControlGroup;
get isValid() { return this.form.controls[this.question.key].valid; }
}
在DynamicForm中我们看到,它通过属性@Input() questions来接收一个数组形式的问题列表。创建QuestionService用于获取问卷中的问题:
// app/question.service.ts

import {Injectable} from 'angular2/core';
import {QuestionBase} from './question-base';
import {DynamicForm} from './dynamic-form.component';
import {TextboxQuestion} from './question-textbox';
import {DropdownQuestion} from './question-dropdown';

@Injectable()
export class QuestionService {

// Todo: get from a remote source of question metadata
// Todo: make asynchronous
getQuestions() {

let questions:QuestionBase<any>[] = [

new DropdownQuestion({
key:'brave',
label: 'Bravery Rating',
options: [
{key:'solid', value:'Solid'},
{key:'great', value:'Great'},
{key:'good', value:'Good'},
{key:'unproven',value:'Unproven'}
],
order: 3
}),

new TextboxQuestion({
key:'firstName',
label:'First name',
value:'Bombasto',
required: true,
order: 1
}),

new TextboxQuestion({
key:'emailAddress',
label:'Email',
type: 'email',
order: 2
})
];

return questions.sort((a,b) => a.order – b.order);
}
}
最后,在AppComponent中显示表单:
// app.component.ts

import {Component} from 'angular2/core'
import {DynamicForm} from './dynamic-form.component';
import {QuestionService} from './question.service';

@Component({
selector: 'my-app',
template: `
<div>
<h2>Dynamic Form Demo</h2>
<dynamic-form [questions]="questions"></dynamic-form>
</div>
`,
directives: [DynamicForm],
providers: [QuestionService]
})
export class AppComponent {
questions:any[]

constructor(service: QuestionService) {
this.questions = service.getQuestions();
}
}
最终的效果如下:

源码

参考资料:
Angular官方文档

最后编辑:
作者:
这个作者貌似有点懒,什么都没有留下。

留下一个回复