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 相关文章推荐
jquery 模式对话框终极版实现代码
Sep 28 Javascript
三级下拉菜单的js实现代码
May 23 Javascript
js调试系列 初识控制台
Jun 18 Javascript
通过JS动态创建一个html DOM元素并显示
Oct 15 Javascript
JavaScript分秒倒计时器实现方法
Feb 02 Javascript
JavaScript模板引擎用法实例
Jul 10 Javascript
Angularjs过滤器使用详解
May 25 Javascript
使用Vue.js创建一个时间跟踪的单页应用
Nov 28 Javascript
BetterScroll 在移动端滚动场景的应用
Sep 18 Javascript
在vue中,v-for的索引index在html中的使用方法
Mar 06 Javascript
element-ui 设置菜单栏展开的方法
Aug 22 Javascript
jQuery实现点击滚动到指定元素上的方法分析
Mar 19 jQuery
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
优化NFR之一 --MSSQL Hello Buffer Overflow
2006/10/09 PHP
PHP两种去掉数组重复值的方法比较
2014/06/19 PHP
如何打开php的gd2库
2017/02/09 PHP
TP5框架实现的数据库备份功能示例
2020/04/05 PHP
一段多浏览器的&quot;复制到剪贴板&quot;javascript代码
2007/03/27 Javascript
JavaScript脚本性能优化注意事项
2008/11/18 Javascript
js限制文本框输入长度两种限制方式(长度、字节数)
2012/12/19 Javascript
js 获取计算后的样式写法及注意事项
2013/02/25 Javascript
JQuery为textarea添加maxlength属性并且兼容IE
2013/04/25 Javascript
jquery实现简单易懂的图片展示小例子
2013/11/21 Javascript
js处理表格对table进行修饰
2014/05/26 Javascript
JavaScript必知必会(六) delete in instanceof
2016/06/08 Javascript
javascript实现简单的on事件绑定
2016/08/23 Javascript
bootstrap table配置参数例子
2017/01/05 Javascript
laravel5.3 vue 实现收藏夹功能实例详解
2018/01/21 Javascript
JS弹窗 JS弹出DIV并使整个页面背景变暗功能的实现代码
2018/04/21 Javascript
JS module的导出和导入的实现代码
2019/02/25 Javascript
压缩Vue.js打包后的体积方法总结(Vue.js打包后体积过大问题)
2020/02/03 Javascript
Python中的yield浅析
2014/06/16 Python
Python实现扫描指定目录下的子目录及文件的方法
2014/07/16 Python
详解python如何调用C/C++底层库与互相传值
2016/08/10 Python
Python实现动态加载模块、类、函数的方法分析
2017/07/18 Python
Python操作mongodb数据库的方法详解
2018/12/08 Python
用django-allauth实现第三方登录的示例代码
2019/06/24 Python
python selenium 执行完毕关闭chromedriver进程示例
2019/11/15 Python
使用Python画出小人发射爱心的代码
2019/11/23 Python
基于css3 animate制作绚丽的动画效果
2015/11/24 HTML / CSS
override和overload的区别
2016/03/09 面试题
大学生开西餐厅创业计划书
2014/02/01 职场文书
煤矿安全承诺书
2014/05/22 职场文书
党员民主评议个人总结
2014/10/20 职场文书
大学生违纪检讨书300字
2014/10/25 职场文书
吃空饷专项整治方案
2014/10/27 职场文书
python如何在word中存储本地图片
2021/04/07 Python
给numpy.array增加维度的超简单方法
2021/06/02 Python
【海涛教你打DOTA】黑鸟第一视角解说
2022/04/01 DOTA