Angular使用总结 --- 模型驱动表单
模型驱动表单
之前有篇博文总结了 模版驱动表单 , 以及 模版驱动表单的自定义校验 , 本篇总结下模型驱动表单。
与模版驱动表单是不同的编程思路,偏向于数据模型。先在组件中建立表单控件的对象树,再绑定到组件模版的原生表单控件上。而模版驱动表单则是在组件模版中使用了内置的 ngForm、ngModel指令,这些指令会自动完成很多工作,以达到双向绑定、监听form和表单控件的状态等等 的目的。虽然模版驱动表单写起来更见的简洁方便,因为指令自动完成了很多工作,但是也正式由于委托指令,所以会导致异步的问题。官网描述 如下:
响应式表单是同步的而模板驱动表单是异步的。 使用响应式表单,你会在代码中创建整个表单控件树。 你可以立即更新一个值或者深入到表单中的任意节点,因为所有的控件都始终是可用的。 模板驱动表单会委托指令来创建它们的表单控件。 为了消除“检查完后又变化了”的错误,这些指令需要消耗一个以上的变更检测周期来构建整个控件树。 这意味着在从组件类中操纵任何控件之前,你都必须先等待一个节拍。
虽然目前本人目前使用模版驱动表单,还没有遇到因异步导致的问题,但是也许在某一天,bug会从天而降。
模型驱动表单需要引入模块 : ReactiveFormsModule 。而模版驱动表单需要引入 FormsModule。
Angular提供了一些方法来构建表单控件的对象树。FormControl,FormGroup,FormArray是构建表单模型的三种表单类,它们有共同的基类 AbstractControl。这三种表单类有不同的作用。
FormControl
FormControl用来构建一个单独的表单控件的值和状态,它会对应这模型中的一个表单元素。FormControl类的构造函数如下:
constructor(formState?: any, validatorOrOpts?: ValidatorFn | ValidatorFn[] | AbstractControlOptions | null, asyncValidator?: AsyncValidatorFn | AsyncValidatorFn[] | null);
接受三个可选参数:初始值、验证器、异步验证器。简单的创建一个formControl
//引入依赖
import { FormControl , Validators} from '@angular/forms'; //创建对象 设置初始值,和校验规则
public name = new FormControl('' , Validators.required);
使用 formControl 可将此控件对象绑定到组件模板中
<input class="form-control" type="text" id="login-name" placeholder="请输入登录帐号" [formControl]="name">
随便输入内容,已经能正确监听控件的值和状态了!每当输入框内容中有变化,name(控件对象)都会随之改变。

此时都是输入内容变化都会触发控件对象更新,那么如何修改触发更新的时机呢?在FormControl的构造函数中有 AbstractControlOptions ,其中的 updateOn 配置项可以修改触发时机。从源码中可以查看到有三种不同的方式, 值变化,失焦,提交。

// 失焦时触发更新
public name = new FormControl('' , {
validators : Validators.required,
updateOn : 'blur'
});
FormGroup
FormGroup对象用来跟踪一组 AbstractControl 的值和状态,即可以跟踪多个formControl和FormGroup。构造函数如下:
constructor(controls: {
[key: string]: AbstractControl;
}, validatorOrOpts?: ValidatorFn | ValidatorFn[] | AbstractControlOptions | null, asyncValidator?: AsyncValidatorFn | AsyncValidatorFn[] | null);
创建一个简单的formGroup,并绑定到模版中
// 简单的formGroup
public loginForm = new FormGroup({
name : new FormControl('', Validators.maxLength(20)),
psw : new FormControl('')
}, Validators.required); // 绑定到模版中
<form class="login-area" (submit)="login()" [formGroup] = "loginForm">
<div class="form-group">
<input class="form-control" type="text" id="login-name" placeholder="请输入登录帐号" formControlName = 'name'>
</div>
<div class="form-group">
<input class="form-control" type="password" id="login-pwd" placeholder="请输入登录密码" formControlName = 'psw'>
</div>
<div class="form-group">
{{loginForm.value | json}}
</div>
</form>
这里就不能再使用[formControl]来绑定表单控件了,因为这里创建的是FormGroup,已经通过[formGroup]绑定到了 form上了,这你需要通过FormGroup来找到FormControl,在表单控件中用formControlName来指定对应的FormControl对象。
FormBuilder
以上的方式构建表单对象树太过于繁琐,需要使用很多的new FormGroup() (构建多级表单的时候) 和 new FromControl() ,FormBuilder就是简化构造方式的的服务。FormBuilder服务中有三个工厂函数 group() control() array() 可以简化代码量。使用FormBuilder的方式再次构建上面的表单对象树。
public loginForm: FormGroup; constructor(
private fb: FormBuilder
) {
已经减少了一些代码量,在表单很庞大的时候,对比会特别明显。注意group工具函数指定表单组的校验规则时与原new GroupForm()参数放置的位置不一样。
/**
* Construct a new {@link FormGroup} with the given map of configuration.
* Valid keys for the `extra` parameter map are `validator` and `asyncValidator`.
*
* See the {@link FormGroup} constructor for more details.
*/
group(controlsConfig: {
[key: string]: any;
}, extra?: {
[key: string]: any;
} | null): FormGroup;
多级FormGroup
为了更高效的管理表单,可以把类似的表单放在一起,形成一个FormGroup,这样就可以同时管理这些类似表单的值和状态。这样创建就会产生多级FormGroup。再通过formGroupName把子group导向模版中。
this.loginForm = fb.group({
name: this.fb.group({
firstname: ['' , Validators.required],
lastname: ''
}),
psw: ['' , Validators.required]
});
<div formGroupName="name">
<div class="form-group">
<input class="form-control" type="text" id="login-name" placeholder="请输入姓" formControlName = 'firstname'>
</div>
<div class="form-group">
<input class="form-control" type="text" id="login-name" placeholder="请输入名" formControlName = 'lastname'>
</div>
</div>
获取控件状态
在FormGroup中可以通过get()方法定位到表单控件对象,然后就能够获取到各种状态了,和之前模版驱动表单中介绍的一样。可以简单的封装一个获取函数,这样在模版中就能非常方便的获取到各个formControl。
getFormControl(name: string) {
return this.loginForm.get(name);
}
<div class="form-group">
{{getFormControl('name').status}}
</div>
<div class="form-group">
{{getFormControl('name.firstname').status}}
</div>
自定义校验
模型驱动表单的自定义校验方便很多,因为可以直接自定义各级formControl。以上面的多级group为例,添加定义的校验规则:firstname和lastname不能输入相同的值。
sameName (): ValidatorFn {
return (control: AbstractControl): { [key: string]: any} => { // control指向使用它的formControl或者formGroup
return control.get('firstname').value === control.get('lastname').value ? { 'sameName' : true } : null; // 子formcontrol的值相同就返回错误
};
}
在模型中使用此校验规则
this.loginForm = fb.group({
name: this.fb.group({
firstname: ['' , Validators.required],
lastname: ['']
} , {
validator : [ // 对整个name的校验规则
this.sameName(), Validators.minLength(2)
]
}),
psw: ['' , Validators.required]
});
Angular使用总结 --- 模型驱动表单的更多相关文章
- Angular之响应式表单 ( Reactive Forms )
项目结构 一 首页 ( index.html ) <!doctype html> <html lang="en"> <head> <met ...
- Angular将填入表单的数据渲染到表格
一.项目简介 我们将采用Angular框架来做一个demo,这个demo将要实现的功能如下: 在X坐标和Y坐标文本框输入信息,然后点击添加,就会在下面表格 中出现一项相应的数据,点击每一项旁边的删除按 ...
- angular 响应式自定义表单控件—注册头像实例
1. 组件继承ControlValueAccessor,ControlValueAccessor接口需要实现三个必选方法 writeValue() 用于向元素中写入值,获取表单的元素的元素值 regi ...
- Angular Reactive Form - 填充表单模型
setValue 使用setValue,可以通过传递其属性与FormGroup后面的表单模型完全匹配的数据对象来一次分配每个表单控件值. 在分配任何表单控件值之前,setValue方法会彻底检查数据对 ...
- angular $http 与form表单的select-->refine
<!DOCTYPE html> <html ng-app="a2_15"> <head> <meta http-equiv="C ...
- angular $http 与form表单的select
产品线 产品 版本 代码是联动关系 ng-model 绑定数据 设置默认值 ng-options 填充option ng-change 选项变化时的操作截图如下: html <!DOCTYPE ...
- Angular使用总结 --- 模版驱动表单
表单的重要性就不多说了,Angular支持表单的双向数据绑定,校验,状态管理等,总结下. 获取用户输入 <div class="container-fluid login-page&q ...
- angular表单知识点
原文 https://www.jianshu.com/p/c772d143e1fc 大纲 1.对表单的理解 2.模板驱动表单(Template Driven Forms) 3.响应式表单(Reacti ...
- 从浅入深剖析angular表单验证
最近手上维护的组件剩下的BUG都是表单验证,而且公司的表单验证那块代码经历的几代人,里面的逻辑开始变得不清晰,而且代码结构不是很angular. 是很有必要深入了解表单验证. 入门之前,我觉得应该先了 ...
随机推荐
- Java 学习之集合类(Collections)
Collection(集合类) 我们是使用数组来保存数据,但是他的长度一旦创建,就已经确定了,当我们要动态传入穿值,数组就有些局限了,集合类就孕育而生:所谓集合,就是来保存,盛装数据,也可称为容器类: ...
- MYSQL 事务测试
mysql 事务测试 创建张表 lock1 增加字段 id,name . 增加两条记录 1,a 2,b 启动第一个会话 BEGIN; update lock1 set name='c' where i ...
- 证明抛物线焦点发出的光线经y=ax^2反射后平行于y轴
- shell 常见面试
1.求100以内的质数 #!/bin/bash n= ;i<=n;i++)) do ;x<=i;x++)) do b=$(( $i%$x )) ]]; then a=$a+ fi done ...
- VBA编程中的 sheet1 与 sheets(1)的区别
[自己理解]sheet1是一个专有名词,不是任何对象的属性,只能单独使用,特指代码所在工作簿的那个sheet1(和顺序无关,是固定的一个表,sheets(1)则和顺序有关). 参考资料: 1.代码中一 ...
- T-SQL查询的逻辑处理过程
对于一个查询SQL而言,通常将其逻辑处理过程分成7个大的阶段 分别是: 1.FROM 2.WHERE 3.GROUP BY 4.HAVING 5.SELECT 6.ORDER BY 7.TOP/OFF ...
- VIP之FrameBuffer
2.VIP Frame Buffer 1.原来我是一直存在一个疑惑,demo上说VIP Frame Buffer输出是固定的60fps,但是在NiosII的程序中我没有找到设置输出为60fps的设置 ...
- Java技术----Java泛型详解
1.为什么需要泛型 泛型在Java中有很重要的地位,网上很多文章罗列各种理论,不便于理解,本篇将立足于代码介绍.总结了关于泛型的知识.希望能给你带来一些帮助. 先看下面的代码: List list = ...
- C#、.Net经典面试题目及答案
1 请你说说 .NET 中类和结构的区别? 答:结构和类具有大体的语法,但是结构受到的限制比类要多.结构不能申明有默认的构造函数,为结构的副本是又编译器创建 和销毁的,所以不需要默认的构造函数和 ...
- 软件工程课堂练习&课下作业
题目:返回一个整数数组中最大子数组的和.一.设计思路按顺序定义子数组的和,如果为负,则选下一位数为子数组的和,反之则两个相加为子数组的和.二.源代码 package test;import java. ...