Angular中响应式表单的三种更新值方法详析


Posted in Javascript onAugust 22, 2017

前言

众所周知Angular响应式表单相比较模板驱动表单更大操作性、更易测试性。因此,我更推荐这类表单创造方式。

当一个用于修改用户信息的表单,数据的来源总是来自远程;而对于一个 FormGroup 的创建总在 ngOnInit 中完成。因此,这里会有一个表单更新值的问题。

而通常我们会透过 FormGroup 下的三种方式 setValue、patchValue、reset 将值写入表单当中。

当然,或许我说的这三种方式时你压根就没有做过,那说明在表单上你依赖的是双向绑定 [(ngModel)],这本身就不是符合 Angular 响应式表单的牛B之处了。因此,在此我们不讨论这种这种方式。下面来一起看看详细的介绍:

一、创建响应式表单

我们模拟一个用户信息修改的表单所需要的字段,可能包括:email、nickname 等。

如果以API的方式与现实字段之间产生一个关联,那么 FormGroup 表示一个表单,FormControl 表示表单中的字段。因此,FormControl 必须包裹在 FromGroup 下面。

下面,我们先简单的构建一个响应式表单。

别忘记导入 ReactiveFormsModule 模块。

@Component({
 selector: 'app-validation',
 template: `
 <form [formGroup]="form" (ngSubmit)="_submitForm(form)">
 <input type="email" formControlName="email">
 <input type="text" formControlName="nickname">
 <button type="submit" [disabled]="form.invalid">Submit</button>
 </form>
 `
})
export class UserEditComponent {
 constructor(private fb: FormBuilder, private route: ActivatedRoute) {}
 
 ngOnInit() {
 this.form = this.fb.group({
  email: ['', Validators.compose([Validators.required, Validators.email])],
  nickname: ['', [Validators.required]]
 });
 
 this.route.params
  .switchMap((params: Params) => loadUser(+params['id']))
  .subscribe(data => {
  // Updating value
  });
 }
 
 loadUser() {
 return Observable.of({ email: 'xx@xx.com', nickname: 'cipchk' }).delay(1000);
 }
 
 _submitForm({ value }) {
 // Save value
 }
}

以上的这些代码再熟悉不过了。假设 UserEditComponent 是由路由 /user/edit/1 触发,那么会发生几个几件事情。

首先,创建一个空的响应式表单 form。

this.form = this.fb.group({
 email: ['', Validators.compose([Validators.required, Validators.email])],
 nickname: ['', [Validators.required]]
});

表单的内容有 email、nickname 两个字段。

  • email 必填项且必须是标准 Email 格式。
  • nickname 必填项。

然而,HTML中,除了 formGroup、formControlName 的配置以外,也看不到任何有关对表单的校验代码。但,当我们输入一个无效 Email 时 input 会自动加上 ng-invalid 类。

这便是响应式表单的魅力。

现在我们回到正题,将分别针对 setValue、patchValue、reset 三种不同更新表单值实际上会发生什么。

二、patchValue

正如名称那般,打补丁。假如我们在 email 文本框里输入:xx@xx.com,接着调用:

this.form.patchValue({ nickname: 'cipchk' });

最终的结果是两个字段同时拥有值,因为这里我们只对 nickname 设置了值,而 email 并没有,那只是先前人为录入的数据。

那么 patchValue 实际上做了什么呢?

patchValue(value: {[key: string]: any}, options: {onlySelf?: boolean, emitEvent?: boolean} = {}): void {
 Object.keys(value).forEach(name => {
 if (this.controls[name]) {
 this.controls[name].patchValue(value[name], {onlySelf: true, emitEvent: options.emitEvent});
 }
 });
 this.updateValueAndValidity(options);
}

首先,利用 Object.keys 查找主键,并以主键名查找相应的 FromControl 实例对象:

Object.keys({ nickname: 'cipchk' }).forEach(name => { 
 console.log(name); 
});
// [ 'nickname' ]

然后,更新值:

this.controls[name].patchValue(value[name], {onlySelf: true, emitEvent: options.emitEvent});

而 FromControl 实例的 patchValue 和 FromGroup 不同,他只是单纯的更新 FromControle 实例对象中的 value 值。

value 相当于表单实际值,还记得先前HTML中的 formControlName 就是将实例与DOM产生联系,这也就是为什么不需要在DOM中使用双向绑定的原因。

三、setValue

跟 patchValue 有一点不一样,当我们提供一个 FromGroup 中并不存在的字段时,会抛出一个错误。除此之外,与 patchValue 并无不同。

setValue(value: {[key: string]: any}, options: {onlySelf?: boolean, emitEvent?: boolean} = {}): void {
 this._checkAllValuesPresent(value);
 Object.keys(value).forEach(name => {
 this._throwIfControlMissing(name);
 this.controls[name].setValue(value[name], {onlySelf: true, emitEvent: options.emitEvent});
 });
 this.updateValueAndValidity(options);
}

主要是 this._throwIfControlMissing(name); 当传递的对象有一个不是 FromControl 时直接抛弃一个 Error。

_throwIfControlMissing(name: string): void {
 if (!Object.keys(this.controls).length) {
 throw new Error(`
 There are no form controls registered with this group yet. If you're using ngModel,
 you may want to check next tick (e.g. use setTimeout).
 `);
 }
 if (!this.controls[name]) {
 throw new Error(`Cannot find form control with name: ${name}.`);
 }
}

四、reset

正常情况下,表单需要提供一个重置按钮时调用此方法。

reset(formState: any = null, options: {onlySelf?: boolean, emitEvent?: boolean} = {}): void {
 this._applyFormState(formState);
 this.markAsPristine(options);
 this.markAsUntouched(options);
 this.setValue(this._value, options);
}

除了恢复校验状态以外。最后一句代码是调用 setValue,这等同上一节说的。因此,当我们调用此方法时,允许我们直接传递一个数据对象做为重置后的默认值,比如:

<button (click)="form.reset({ nickname: 'xx' })">Reset</button>

重置表单后并设置 nickname 默认值为:xx。

结论

每一种不同更新值方式都会有不一样的结果,当我们回头过看开头中留下来的:

// Updating value

如果是你,你会怎么写呢?

总结

以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作能带来一定的帮助,如果有疑问大家可以留言交流,谢谢大家对三水点靠木的支持。

Javascript 相关文章推荐
javascript检测对象中是否存在某个属性判断方法小结
May 19 Javascript
jquery实现table鼠标经过变色代码
Sep 25 Javascript
jquery实现勾选复选框触发事件给input赋值
Feb 01 Javascript
基于JQuery实现图片轮播效果(焦点图)
Feb 02 Javascript
AngularJS身份验证的方法
Feb 17 Javascript
js中获取键盘事件的简单实现方法
Oct 10 Javascript
原生js开发的日历插件
Feb 04 Javascript
vue文件树组件使用详解
Mar 29 Javascript
vue生成token并保存到本地存储中
Jul 17 Javascript
通过js示例讲解时间复杂度与空间复杂度
Aug 06 Javascript
layui的表单验证支持ajax判断用户名是否重复的实例
Sep 06 Javascript
vue响应式原理与双向数据的深入解析
Jun 04 Vue.js
jQuery插件DataTables分页开发心得体会
Aug 22 #jQuery
React学习笔记之列表渲染示例详解
Aug 22 #Javascript
bootstrap switch开关组件使用方法详解
Aug 22 #Javascript
Javascript中toFixed计算错误(依赖银行家舍入法的缺陷)解决方法
Aug 22 #Javascript
jQuery实现广告条滚动效果
Aug 22 #jQuery
基于jQuery的表单填充实例
Aug 22 #jQuery
JS倒计时实例_天时分秒
Aug 22 #Javascript
You might like
PHP4与PHP5的时间格式问题
2008/02/17 PHP
php 不使用js实现页面跳转
2014/02/11 PHP
PHP 设计模式系列之 specification规格模式
2016/01/10 PHP
Symfony2使用Doctrine进行数据库查询方法实例总结
2016/03/18 PHP
PHP接口继承及接口多继承原理与实现方法详解
2017/10/18 PHP
jquery学习笔记二 实现可编辑的表格
2010/04/09 Javascript
关于html+ashx开发中几个问题的解决方法
2011/07/18 Javascript
新浪微博字数统计 textarea字数统计实现代码
2011/08/28 Javascript
JS window对象的top、parent、opener含义介绍
2013/12/03 Javascript
JQuery中$(document)是什么意思有什么作用
2014/07/21 Javascript
iframe跨域通信封装详解
2015/08/11 Javascript
Javascript基础教程之比较null和undefined值
2016/05/16 Javascript
BootStrap 智能表单实战系列(二)BootStrap支持的类型简介
2016/06/13 Javascript
javascript实现随机生成DIV背景色
2016/06/20 Javascript
onmouseover事件和onmouseout事件全面理解
2016/08/15 Javascript
Javascript中indexOf()和lastIndexOf应用方法实例
2016/08/24 Javascript
canvas实现粒子时钟效果
2017/02/06 Javascript
jQuery validate 验证radio实例
2017/03/01 Javascript
详解Require.js与Sea.js的区别
2018/08/05 Javascript
Webpack中SplitChunksPlugin 配置参数详解
2020/03/24 Javascript
VUE使用axios调用后台API接口的方法
2020/08/03 Javascript
OpenCV实现人脸识别
2017/04/07 Python
Django 导出 Excel 代码的实例详解
2017/08/11 Python
浅谈Python peewee 使用经验
2017/10/20 Python
Python如何发布程序的详细教程
2018/10/09 Python
启动Atom并运行python文件的步骤
2018/11/09 Python
python使用sklearn实现决策树的方法示例
2019/09/12 Python
python实现在多维数组中挑选符合条件的全部元素
2019/11/26 Python
基于Python执行dos命令并获取输出的结果
2019/12/30 Python
凯特·丝蓓英国官网:Kate Spade英国
2016/11/07 全球购物
英国办公家具网站:Furniture At Work
2019/10/07 全球购物
毕业生就业自荐书
2013/12/15 职场文书
大学生职业生涯规划书
2014/03/14 职场文书
5s推行计划书
2014/05/06 职场文书
公司放假通知范文
2015/04/14 职场文书
Redis高级数据类型Hyperloglog、Bitmap的使用
2021/05/24 Redis