在使用ng-zorro的表单时,发现他和angular的表单有很大不一样,因而就去学习了一下angular的表单。
在angular中表单有两种形式,一种是模板驱动表单,一种是响应式表单,模板驱动表单跟angularjs的表单差很少,都是在模板中进行数据绑定,验证等,而响应式表单是功能更强大,灵活的表单形式。css
响应式表单是用模型驱动来处理表单与用户交互的,在响应式表单中每一个表单控件都是一个模型。使用响应式表单时,须要在导入@angular/forms 包中导入 ReactiveFormsModule并在模块中加入imports数组中.html
在组件类中导入FormControl类,建立一个FromControl类实例,他将关联到表单中的一个控件.angularjs
'json
以后在模板中用fromControl指令把模板中表单控件关联到name对象:``数组
<label> Name: <input class="form-control" type="text" [formControl]="name"> </label> <p> {{name.value}} </p>
这个时候咱们的name对象就关联到了模板中的input表单控件了,用name.value属性就能够看到对象的值,他是与视图值绑定在一块儿的.此时这个input控件就由name这个模型管理,获取值,修改值,验证都经过这个模型进行.app
在表单中,一般有多个控件,把多个控件合并在一块儿有助于管理,能够用FormGroup来管理控件FormContrl.
从@angular/forms包中导入FormGroup,新建一个FormGroup对象,在构造函数中传入一个对象,属性名表明控件的名字,值就是一个FormContrl实例.ide
在模板的表单中用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服务来简化建立过程:
导入@angular/forms 包中导入 FormBuilder类,在构造函数中注入服务,使用服务方法来简化生成过程:学习
constructor(private fb: FormBuilder) { }
生成对比:ui
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来循环生成下拉框
咱们须要把这个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对象数组来定义咱们须要的表单控件,验证信息也能够经过这样相似的方法生成,只须要提供表单控件各自的属性,统一辈子成表单控件,便于维护和编写。