angular响应式表单

在使用ng-zorro的表单时,发现他和angular的表单有很大不同,于是就去学习了一下angular的表单。
在angular中表单有两种形式,一种是模板驱动表单,一种是响应式表单,模板驱动表单跟angularjs的表单差不多,都是在模板中进行数据绑定,验证等,而响应式表单是功能更强大,灵活的表单形式。

FormControl表示表单控件

响应式表单是用模型驱动来处理表单与用户交互的,在响应式表单中每个表单控件都是一个模型。使用响应式表单时,需要在导入@angular/forms 包中导入 ReactiveFormsModule并在模块中加入imports数组中.

angular响应式表单_第1张图片

创建一个表单控件

在组件类中导入FormControl类,创建一个FromControl类实例,他将关联到表单中的一个控件.

angular响应式表单_第2张图片'

之后在模板中用fromControl指令把模板中表单控件关联到name对象:``


{{name.value}}

angular响应式表单_第3张图片

这个时候我们的name对象就关联到了模板中的input表单控件了,用name.value属性就可以看到对象的值,他是与视图值绑定在一起的.此时这个input控件就由name这个模型管理,获取值,修改值,验证都通过这个模型进行.

FolmGroup管理FormContrl

将控件合并

在表单中,通常有多个控件,把多个控件合并在一起有助于管理,可以用FormGroup来管理控件FormContrl.
从@angular/forms包中导入FormGroup,新建一个FormGroup对象,在构造函数中传入一个对象,属性名代表控件的名字,值就是一个FormContrl实例.

angular响应式表单_第4张图片

关联FormGroup模型到模板视图

在模板的表单中用FormGroup指令来关联模型,由 FormControlName指令提供的formControlName属性把每个输入框和 FormGroup 中定义的表单控件绑定起来。这样在视图表单控件值修改时,会反应到FormGroup上.

{{teacher.value|json}}

angular响应式表单_第5张图片

用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下拉框:

angular响应式表单_第6张图片

这两个控件有很多的共同点,他们都是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[] ) {
    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;
  @Input() form: FormGroup;
  constructor() { }

  ngOnInit() {
  }

}

这个组件类首先接受一个抽象的基类对象和一个FormGoup,用Input()获取,然后再在模板中根据element生成相应的控件:

用ngSwitch来判断,如果当前的控件html属性为input显示,为select显示