angular響應式表單

在使用ng-zorro的表單時,發現他和angular的表單有很大不一樣,因而就去學習了一下angular的表單。
在angular中表單有兩種形式,一種是模板驅動表單,一種是響應式表單,模板驅動表單跟angularjs的表單差很少,都是在模板中進行數據綁定,驗證等,而響應式表單是功能更強大,靈活的表單形式。css

FormControl表示表單控件

響應式表單是用模型驅動來處理表單與用戶交互的,在響應式表單中每一個表單控件都是一個模型。使用響應式表單時,須要在導入@angular/forms 包中導入 ReactiveFormsModule並在模塊中加入imports數組中.html

clipboard.png

建立一個表單控件

在組件類中導入FormControl類,建立一個FromControl類實例,他將關聯到表單中的一個控件.angularjs

clipboard.png'json

以後在模板中用fromControl指令把模板中表單控件關聯到name對象:``數組

<label>
  Name:
  <input class="form-control" type="text" [formControl]="name">
</label>
<p>
  {{name.value}}
</p>

clipboard.png

這個時候咱們的name對象就關聯到了模板中的input表單控件了,用name.value屬性就能夠看到對象的值,他是與視圖值綁定在一塊兒的.此時這個input控件就由name這個模型管理,獲取值,修改值,驗證都經過這個模型進行.app

FolmGroup管理FormContrl

將控件合併

在表單中,一般有多個控件,把多個控件合併在一塊兒有助於管理,能夠用FormGroup來管理控件FormContrl.
從@angular/forms包中導入FormGroup,新建一個FormGroup對象,在構造函數中傳入一個對象,屬性名錶明控件的名字,值就是一個FormContrl實例.ide

clipboard.png

關聯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>

clipboard.png

用FormBuilder簡化生成控件

由於表單使用很頻繁,手動建立多個表單控件會很是繁瑣,可使用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下拉框:

clipboard.png

這兩個控件有不少的共同點,他們都是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組件會根據遍歷對象生成相應的控件並綁定.
效果:

clipboard.png]能夠用服務來生成BaseOb對象數組來定義咱們須要的表單控件,驗證信息也能夠經過這樣相似的方法生成,只須要提供表單控件各自的屬性,統一輩子成表單控件,便於維護和編寫。

相關文章
相關標籤/搜索