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面向对象入门基础详细介绍
Sep 05 Javascript
javascript正则表达式中的replace方法详解
Apr 20 Javascript
Jsonp post 跨域方案
Jul 06 Javascript
解决jQuery uploadify在非IE核心浏览器下无法上传
Aug 05 Javascript
基于jQuery实现网页打印功能
Dec 01 Javascript
jQuery EasyUi实战教程之布局篇
Jan 26 Javascript
Bootstrop实现多级下拉菜单功能
Nov 24 Javascript
JS实现最简单的冒泡排序算法
Feb 15 Javascript
jQuery.Sumoselect插件实现下拉复选框效果
Nov 09 jQuery
angularjs中判断ng-repeat是否迭代完的实例
Sep 12 Javascript
微信小程序dom操作的替代思路实例分析
Dec 06 Javascript
一起深入理解js中的事件对象
Feb 06 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在多维数组中根据键名快速查询其父键以及父键值的代码
2011/05/07 PHP
PHP中的socket_read和socket_recv区别详解
2015/02/09 PHP
PHP设计模式之命令模式示例详解
2020/12/20 PHP
5款Javascript颜色选择器
2009/10/25 Javascript
jquery中的 $(&quot;#jb51&quot;)与document.getElementById(&quot;jb51&quot;) 的区别
2011/07/26 Javascript
js将iframe中控件的值传到主页面控件中的实现方法
2013/03/11 Javascript
extjs tabpanel限制选项卡数量实现思路及代码
2013/04/02 Javascript
Javascript对象属性方法汇总
2013/11/21 Javascript
js如何调用qq互联api实现第三方登录
2014/03/28 Javascript
jQuery中removeData()方法用法实例
2014/12/27 Javascript
JavaScript中的some()方法使用详解
2015/06/09 Javascript
详解AngularJS Filter(过滤器)用法
2015/12/28 Javascript
JS遍历数组及打印数组实例分析
2016/01/21 Javascript
原生js封装二级城市下拉列表的实现代码
2016/06/16 Javascript
js数组与字符串常用方法总结
2017/01/13 Javascript
解决Node.js使用MySQL出现connect ECONNREFUSED 127.0.0.1:3306的问题
2017/03/09 Javascript
微信小程序框架wepy之动态控制类名
2018/09/14 Javascript
vue路由事件beforeRouteLeave及组件内定时器的清除方法
2018/09/29 Javascript
vue获取验证码倒计时组件
2019/08/26 Javascript
vuex存储token示例
2019/11/11 Javascript
[01:02:53]DOTA2上海特级锦标赛主赛事日 - 5 总决赛Liquid VS Secret第二局
2016/03/06 DOTA
python pandas中DataFrame类型数据操作函数的方法
2018/04/08 Python
Python的argparse库使用详解
2018/10/09 Python
详解Python3网络爬虫(二):利用urllib.urlopen向有道翻译发送数据获得翻译结果
2019/05/07 Python
pytorch实现focal loss的两种方式小结
2020/01/02 Python
Python 实现将数组/矩阵转换成Image类
2020/01/09 Python
python 爬虫 实现增量去重和定时爬取实例
2020/02/28 Python
PyTorch之nn.ReLU与F.ReLU的区别介绍
2020/06/27 Python
澳大利亚自然和有机的健康美容产品一站式商店:Ziani Beauty
2017/12/28 全球购物
亚马逊巴西站:Amazon.com.br
2019/09/22 全球购物
Java中各种基本数据类型的默认值都是什么
2016/12/22 面试题
宣传普通话标语
2014/06/27 职场文书
助学金感谢信
2015/01/20 职场文书
暑期社会实践个人总结
2015/03/06 职场文书
东京审判观后感
2015/06/01 职场文书
LeetCode189轮转数组python示例
2022/08/05 Python