在使用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對象數組來定義咱們須要的表單控件,驗證信息也能夠經過這樣相似的方法生成,只須要提供表單控件各自的屬性,統一輩子成表單控件,便於維護和編寫。