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的闭包
Dec 31 Javascript
JavaScript NaN和Infinity特殊值 [译]
Sep 20 Javascript
Js实现网页键盘控制翻页的方法
Oct 30 Javascript
JS实现的左侧竖向滑动菜单效果代码
Oct 19 Javascript
jQuery layui常用方法介绍
Jul 25 Javascript
Bootstrap CSS组件之大屏幕展播
Dec 17 Javascript
vue.js利用Object.defineProperty实现双向绑定
Mar 09 Javascript
Node.js中的http请求客户端示例(request client)
May 04 Javascript
浅谈如何使用webpack构建多页面应用
May 30 Javascript
JavaScript设计模式之模板方法模式原理与用法示例
Aug 07 Javascript
详解离线安装npm包的几种方法
Nov 25 Javascript
Taro小程序自定义顶部导航栏功能的实现
Dec 17 Javascript
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
十天学会php(1)
2006/10/09 PHP
php中的字符编码转换函数用法示例
2014/10/20 PHP
Zend Framework过滤器Zend_Filter用法详解
2016/12/09 PHP
PHP+Ajax简单get验证操作示例
2019/03/02 PHP
php设计模式之观察者模式实例详解【星际争霸游戏案例】
2020/03/30 PHP
优化javascript的执行速度
2010/01/23 Javascript
javascript列表框操作函数集合汇总
2013/11/28 Javascript
js replace替换所有匹配的字符串
2014/02/13 Javascript
JS中实现简单Formatter函数示例代码
2014/08/19 Javascript
js 显示日期时间的实例(时间过一秒加1)
2017/10/25 Javascript
动态加载权限管理模块中的Vue组件
2018/01/16 Javascript
JS复杂判断的更优雅写法代码详解
2018/11/07 Javascript
LayUi使用switch开关,动态的去控制它是否被启用的方法
2019/09/21 Javascript
vue iview实现动态新增和删除
2020/06/17 Javascript
[01:33]一分钟玩转DOTA2第三弹:DOTA2&DotA快捷操作大对比
2014/06/04 DOTA
[01:14]DOTA2亚洲邀请赛 ShowOpen
2015/02/07 DOTA
[02:27]2018DOTA2亚洲邀请赛赛前采访-OpTic
2018/04/03 DOTA
python批量下载图片的三种方法
2013/04/22 Python
python获得两个数组交集、并集、差集的方法
2015/03/27 Python
在Python的Flask框架中验证注册用户的Email的方法
2015/09/02 Python
python 处理dataframe中的时间字段方法
2018/04/10 Python
Python实现的生产者、消费者问题完整实例
2018/05/30 Python
pygame游戏之旅 如何制作游戏障碍
2018/11/20 Python
python字典改变value值方法总结
2019/06/21 Python
python networkx 根据图的权重画图实现
2019/07/10 Python
Python代码使用 Pyftpdlib实现FTP服务器功能
2019/07/22 Python
Python3 JSON编码解码方法详解
2019/09/06 Python
Python实现隐马尔可夫模型的前向后向算法的示例代码
2019/12/31 Python
世界著名的顶级牛排:Omaha Steak(奥马哈牛排)
2016/09/20 全球购物
JOSEPH官网:英国奢侈时尚品牌
2018/01/31 全球购物
匈牙利超级网上商店和优惠:Alza.hu
2019/12/17 全球购物
银行职员个人的工作自我评价
2014/02/15 职场文书
星级党支部申报材料
2014/05/31 职场文书
详解nginx.conf 中 root 目录设置问题
2021/04/01 Servers
Python实现照片卡通化
2021/12/06 Python
MySQL笔记 —SQL运算符
2022/01/18 MySQL