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 相关文章推荐
js 对象是否存在判断
Jul 15 Javascript
js onclick事件传参讲解
Nov 06 Javascript
解决JS中乘法的浮点错误的方法
Jan 03 Javascript
JS函数重载的解决方案
May 13 Javascript
php读取sqlite数据库入门实例代码
Jun 25 Javascript
Eclipse配置Javascript开发环境图文教程
Jan 29 Javascript
jQuery带进度条全屏图片轮播特效代码分享
Jun 28 Javascript
bootstrap快速制作后台界面
Dec 05 Javascript
微信小程序上传图片到服务器实例代码
Nov 07 Javascript
Vue.js 使用v-cloak后仍显示变量的解决方法
Nov 19 Javascript
JavaScript面向对象编程小游戏---贪吃蛇代码实例
May 15 Javascript
微信小程序商品详情页底部弹出框
Nov 22 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
ftp类(example.php)
2006/10/09 PHP
php中通过正则表达式下载内容中的远程图片的函数代码
2012/01/10 PHP
phpstorm编辑器乱码问题解决
2014/12/01 PHP
php数字运算验证码的实现代码
2015/07/30 PHP
Symfony2学习笔记之系统路由详解
2016/03/17 PHP
完美解决phpdoc导出文档中@package的warning及Error的错误
2016/05/17 PHP
如何实现iframe(嵌入式帧)的自适应高度
2006/07/26 Javascript
在线编辑器的实现原理(兼容IE和FireFox)
2007/03/09 Javascript
基于jQuery的弹出框插件
2012/03/18 Javascript
javascript基础知识大全 便于大家学习,也便于我自己查看
2012/08/17 Javascript
js判读浏览器是否支持html5的canvas的代码
2013/11/18 Javascript
常用的几段javascript代码分享
2014/03/25 Javascript
js事件绑定快捷键以ctrl+k为例
2014/09/30 Javascript
js实现时间显示几天前、几小时前或者几分钟前的方法集锦
2015/05/29 Javascript
老生常谈遮罩层 滚动条的问题
2016/04/29 Javascript
通过BootStrap-select插件 js jQuery控制select属性变化
2017/01/03 Javascript
基于Angular.js实现的触摸滑动动画实例代码
2017/02/19 Javascript
JavaScript fetch接口案例解析
2018/08/30 Javascript
tracking.js页面人脸识别插件使用方法
2020/04/16 Javascript
在 Django/Flask 开发服务器上使用 HTTPS
2014/07/03 Python
python利用urllib实现爬取京东网站商品图片的爬虫实例
2017/08/24 Python
Python爬虫框架scrapy实现downloader_middleware设置proxy代理功能示例
2018/08/04 Python
python实现简单flappy bird
2018/12/24 Python
Python实现程序判断季节的代码示例
2019/01/28 Python
Python 3.6 -win64环境安装PIL模块的教程
2019/06/20 Python
python实现计算器功能
2019/10/31 Python
如何基于Python获取图片的物理尺寸
2019/11/25 Python
Django 多对多字段的更新和插入数据实例
2020/03/31 Python
英国顶级家庭折扣店:The Works
2017/09/06 全球购物
幼儿园教师培训方案
2014/02/04 职场文书
护理专业求职信
2014/06/15 职场文书
珠宝的促销活动方案
2014/08/31 职场文书
销售员试用期自我评价
2014/09/15 职场文书
护士岗前培训心得体会
2016/01/08 职场文书
Python还能这么玩之只用30行代码从excel提取个人值班表
2021/06/05 Python
postgresql 删除重复数据案例详解
2021/08/02 PostgreSQL