在使用ng-zorro的表单时,发现他和angular的表单有很大不同,于是就去学习了一下angular的表单。
在angular中表单有两种形式,一种是模板驱动表单,一种是响应式表单,模板驱动表单跟angularjs的表单差不多,都是在模板中进行数据绑定,验证等,而响应式表单是功能更强大,灵活的表单形式。
FormControl表示表单控件
响应式表单是用模型驱动来处理表单与用户交互的,在响应式表单中每个表单控件都是一个模型。使用响应式表单时,需要在导入@angular/forms 包中导入 ReactiveFormsModule并在模块中加入imports数组中.
创建一个表单控件
在组件类中导入FormControl类,创建一个FromControl类实例,他将关联到表单中的一个控件.
'
之后在模板中用fromControl指令把模板中表单控件关联到name对象:``
<label>
Name:
<input class="form-control" type="text" [formControl]="name">
</label>
<p>
{{name.value}}
</p>
这个时候我们的name对象就关联到了模板中的input表单控件了,用name.value属性就可以看到对象的值,他是与视图值绑定在一起的.此时这个input控件就由name这个模型管理,获取值,修改值,验证都通过这个模型进行.
FolmGroup管理FormContrl
将控件合并
在表单中,通常有多个控件,把多个控件合并在一起有助于管理,可以用FormGroup来管理控件FormContrl.
从@angular/forms包中导入FormGroup,新建一个FormGroup对象,在构造函数中传入一个对象,属性名代表控件的名字,值就是一个FormContrl实例.
关联FormGroup模型到模板视图
在模板的表单中用FormGroup指令来关联模型,由 FormControlName指令提供的formControlName属性把每个输入框和 FormGroup 中定义的表单控件绑定起来。这样在视图表单控件值修改时,会反应到FormGroup上.
<form [formGroup]="teacher">
<label>
TeacherName:
<input class="form-control" type="text" formControlName="name">
</label>
<label>
TeacherEmail:
<input class="form-control" type="text" formControlName="email">
</label>
</form>
<pre>{{teacher.value|json}}</pre>
用FormBuilder简化生成控件
因为表单使用很频繁,手动创建多个表单控件会非常繁琐,可以使用FoRmBuilder服务来简化创建过程:
导入@angular/forms 包中导入 FormBuilder类,在构造函数中注入服务,使用服务方法来简化生成过程:
constructor(private fb: FormBuilder) {
}
生成对比:
name = new FormControl('');
builderName = this.fb.control('');
teacher = new FormGroup({
name: new FormControl(''),
email: new FormControl('')
});
builderTeacher = this.fb.group({
name: [''],
email: ['']
});
动态表单
在之前使用angularjs开发时,很多表单都很相似,但是却不得不写多个相似的表单,使用响应式表单可以将这些表单都抽象出来,动态生成表单,使得不用写重复的代码,而且更易于维护。
先比较这两个控件,一个input一个select下拉框:
这两个控件有很多的共同点,他们都是html标签,都有一个label,一个id,一个formcontrolName,一个type,且控件都有值(.value),只是他们的值都是不同的,可以把这两个控件抽象成一个基类对象,他有id,lable,html标签类型,value属性,在通过继承基类对象生成对应的控件.
构建对象模型
基类对象
value: T; //控件的值
key: string; //控件名字
label: string; //控件描述
controlType: string; //html标签类型
constructor(options: {
value?: T,
key?: string,
label?: string,
controlType?: string
} = {}) {
this.value = options.value;
this.key = options.key || '';
this.label = options.label || '';
this.controlType = options.controlType || '';
}
input对象
import {BaseOb} from './base-ob';
export class InputText extends BaseOb{
controlType = 'input';
type: string;
constructor(options: {} = {}) {
super(options);
this.type = options['type'] || '';
}
}
通过继承BaseOb对象,并声明自己为input(html类型),加上一个type(text)
select对象:
import {BaseOb} from './base-ob';
export class Select extends BaseOb{
controlType = 'select';
options: {key: string, value: string}[] = [];
constructor(options: {} = {}) {
super(options);
this.options = options['options'] || [];
}
}
select不需要type类型,但他需要一个options来循环生成下拉框
将对象模型数组转化为同一个FormGroup
我们需要把这个input和select转化为一个FormGroup来与模板视图交互,因此需要一个服务把BaseOb数组转化为一个FormGroup对象:
import { Injectable } from '@angular/core';
import {BaseOb} from './base-ob';
import {FormControl, FormGroup} from '@angular/forms';
@Injectable({
providedIn: 'root'
})
export class BaseObService {
constructor() { }
toFormGroup(obs: BaseOb<any>[] ) {
const group: any = {};
obs.forEach(ob => {
group[ob.key] = new FormControl(ob.value || ''); // 以key作为FormControl的名字,value作为值
});
return new FormGroup(group); // 将对象传入formGroup构造函数生成FormGroup
}
}
将模型对象转化为模板视图
有了模型对象,就得把模型对象转化为之前的视图,用一个组件来做这件事:
import {Component, Input, OnInit} from '@angular/core';
import {BaseOb} from '../base-ob';
import {FormGroup} from '@angular/forms';
@Component({
selector: 'app-element',
templateUrl: './element.component.html',
styleUrls: ['./element.component.css']
})
export class ElementComponent implements OnInit {
@Input() element: BaseOb<any>;
@Input() form: FormGroup;
constructor() { }
ngOnInit() {
}
}
这个组件类首先接受一个抽象的基类对象和一个FormGoup,用Input()获取,然后再在模板中根据element生成相应的控件:
<div [formGroup]="form">
<label [attr.for]="element.key">{{element.label}}</label>
<div [ngSwitch]="element.controlType">
<input *ngSwitchCase="'input'" [formControlName]="element.key"
[id]="element.key" [type]="element.type">
<select [id]="element.key" *ngSwitchCase="'select'" [formControlName]="element.key">
<option *ngFor="let opt of element.options" [value]="opt.key">{{opt.value}}</option>
</select>
</div>
</div>
用ngSwitch来判断,如果当前的控件html属性为input显示<input>,为select显示<select>,其余如此.
使用动态表单
通过生成BaseOb数组并用ngfor循环生成<app-element>就可以动态的生成表单控件了:
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent implements OnInit{
obs: BaseOb<any>[];
form: FormGroup;
constructor(private obService: BaseObService) { }
ngOnInit(): void {
this.obs = [
new InputText({
key: 'name',
label: 'teacher name',
value: 'zhansan',
type: 'text'
}),
new Select({
key: 'klass',
label: 'klasses',
options: [
{key: '1', value: '一班'},
{key: '2', value: '二班'},
{key: '3', value: '三班'},
{key: '4', value: '四班'}
]
})
];
this.form = this.obService.toFormGroup(this.obs);
}
}
在组件中新建一个input(text)模型和一个select模型,通过服务获取表单组,之后在组件模板中调用<app-element>
<h2>动态表单生成:</h2>
<div style="width: 200px;">
<form [formGroup]="form">
<div *ngFor="let o of obs">
<app-element [element]="o" [form]="form"></app-element>
</div>
</form>
</div>
<pre>{{form.value|json}}</pre>
循环obs数组,app-element组件会根据遍历对象生成相应的控件并绑定.
效果:
]
可以用服务来生成BaseOb对象数组来定义我们需要的表单控件,验证信息也可以通过这样类似的方法生成,只需要提供表单控件各自的属性,统一生成表单控件,便于维护和编写。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。