[Angular 基础] - 表单:模板驱动表单

[Angular 基础] - 表单:模板驱动表单

之前的笔记:

  • [Angular 基础] - routing 路由(上)

  • [Angular 基础] - routing 路由(下)

  • [Angular 基础] - Observable


Angular 内置两种表单的支持,这篇写的就是第一种,即模板驱动表单 (Template-Driven Form)

Template-Driven Form 的实现比较简单,Angular 自身会生成和提供对应的表单控制和管理状态,对于开发者来说,实现相对而言更加简单,因此更加适合简单的表单实现

做一个平行对比,React 有 uncontrolled form & controlled form,Angular 有一个 Template-Driven Form 和 Reactive form,虽然这么看起来两个实现似乎挺像的,不过本质上还是有些区别的

对于 React 的 uncontrolled form,它本质上是让 HTML 去进行处理,然后可以通过 refs 去获得 HTML 的值,React 不会对其进行太多的干预;对比 Angular 的 Template-Driven Form,Angular 本身通过 绑定 对状态进行了接管

开始

下面是一个表单:

<div class="container"><div class="row"><div class="col-xs-12 col-sm-10 col-md-8 col-sm-offset-1 col-md-offset-2"><form><div id="user-data"><div class="form-group"><label for="username">Username</label><input type="text" id="username" class="form-control" /></div><button class="btn btn-default" type="button">Suggest an Username</button><div class="form-group"><label for="email">Mail</label><input type="email" id="email" class="form-control" /></div></div><div class="form-group"><label for="secret">Secret Questions</label><select id="secret" class="form-control"><option value="pet">Your first Pet?</option><option value="teacher">Your first teacher?</option></select></div><button class="btn btn-primary" type="submit">Submit</button></form></div></div>
</div>

表单渲染如下:

在这里插入图片描述

相别于直接使用 JS+HTML 的方式实现表单,使用 Angular 这里没有添加 action 这一 action,这一步会让 Angular 去安排。

这一点在之前也提过,需要实现这一点,也需要在 app.module.ts 中导入 FormsMorule

绑定控制

这里主要使用 ngModelname="" 绑定所有输入:

<label for="username">Username</label>
<input type="text" id="username" class="form-control" ngModel name="username" /><label for="email">Mail</label>
<input type="email" id="email" class="form-control" ngModel name="email" /><label for="secret">Secret Questions</label>
<select id="secret" class="form-control" ngModel name="secret"><option value="pet">Your first Pet?</option><option value="teacher">Your first teacher?</option>
</select>

我这里省略了其他部分的代码,只留下了对应的 labelinput

⚠️:name 是一个 HTML attribute,这个实现本质上跟 angular 没什么关系。但是在 Template-Driven Form 中它的作用非常的重要。它会用来创建一个 FormControl 的实例对象,对 ngModel 进行自动绑定,这也是为什么这里的 ngModel 使用语法和 2 way binding 里的不一样。可以说,没有 name,那么 Angular 就无法正确识别对应的 ``FormControl`,也无法接管对应的 onChange 和 validation

提交表单 AKA 获取提交的值

这里提交表单的做法不是之前实现的那样,在 submit button 上绑定 onclick 事件,而是使用 angular 提供的 directive 去实现——这也是为什么这种实现叫 Template-Driven Form,代码修改如下:

  • V 层

    <form (ngSubmit)="onSubmit(f)" #f="ngForm"><!-- 其余不变 -->
    </form>
    
  • VM 层

    onSubmit(form: NgForm) {console.log('submitted');console.log(form);
    }
    

输出结果如下:

在这里插入图片描述

注意这里的 local reference 用法,它绑定了一个特殊的 ngForm directive,因此传到 onSubmit 中的值就是 NgForm。默认情况下它的值是当前 HTML 元素类型,表单对应的元素类型是 HTMLFormElement

通过 NgForm 就可以轻松的获取当前表单的值、控制(即 FormControl)、验证等。这些值可以杯统称为当前表单的 状态

验证

这里主要是通过 angular 提供的验证搭配 HTML5 的验证进行实现,代码修改如下:

<div class="form-group"><label for="username">Username</label><inputtype="text"id="username"class="form-control"ngModelname="username"required/>
</div>
<inputtype="email"id="email"class="form-control"ngModelname="email"requiredemail
/>

这里主要用了 angular 提供的 requiredemail 这两个 validators。这个时候表单还是可以继续提交的,输出结果如下:

在这里插入图片描述

可以看到,当前 NgFormsubmittedtrueinvalid 也是 true

这个时候就可以利用一下这个 invalid 属性:

<button class="btn btn-primary" type="submit" [disabled]="f.invalid">Submit
</button>

这样可以阻止用户在表单验证未通过的情况下,点击 submit 事件:

修改报错样式

这里主要通过 CSS 实现,修改代码如下:

input.ng-invalid.ng-touched,
select.ng-invalid.ng-touched {border: 1px solid red;
}

实现效果如下:

在这里插入图片描述

这里也是通过 angular 提供的 class 进行的实现。angular 会在对应的元素上添加对应的状态,touched 指的是用户是否触碰(focus)过当前元素。

报错信息

这里使用 ngIf 搭配对应的 directive 进行报错信息的渲染,代码修改如下:

<div class="form-group"><label for="email">Mail</label><inputtype="email"id="email"class="form-control"ngModelname="email"autocomplete="off"requiredemail#email="ngModel"/><span class="help-block" *ngIf="email.invalid && email.touched">Please enter a valid email!</span>
</div>

效果如下:

在这里插入图片描述

⚠️:这里依旧使用 local reference+绑定 directive 的方法实现,不过这里绑定的是 ngModel 而不是 ``NgForm`

设置默认值

这里主要就是设置一下默认值,使用的是 property binding,代码如下:

<div class="form-group"><label for="secret">Secret Questions</label><select id="secret" class="form-control" [ngModel]="'pet'" name="secret"><option value="pet">Your first Pet?</option><option value="teacher">Your first teacher?</option></select>
</div>

实现效果如下:

在这里插入图片描述

⚠️:这里使用的是 property binding,即 [ngModel] 提供默认值,而不是提供 2-way binding——在有需求的情况下还是可以使用 2-way binding 的。使用 property binding 并不会修改 VM 层中的数据。

👀:也可以在 VM 层实现一个变量,而不是直接将值写到 V 层中

组合 form control

这可以将一些数据组合在一起,比如说地址的组合通常为省+市+具体地址+邮编才能组合成一个完整的地址,实现方法如下:

<div id="user-data" ngModelGroup="userData"><div class="form-group"><label for="username">Username</label><inputtype="text"id="username"class="form-control"ngModelname="username"required/></div><button class="btn btn-default" type="button">Suggest an Username</button><div class="form-group"><label for="email">Mail</label><inputtype="email"id="email"class="form-control"ngModelname="email"autocomplete="off"requiredemail#email="ngModel"/><span class="help-block" *ngIf="email.invalid && email.touched">Please enter a valid email!</span></div>
</div>

效果如下:

在这里插入图片描述

⚠️:这里的语法为 ngModelGroup="string-value"

👀:这里同样可以添加 local reference,如: #userData="ngModelGroup" 这样就可以对整个 group 进行提示,如:

<p *ngIf="userData.invalid && userData.touched">User Data is invalid!</p>

这个报错信息只有在用户提供了正确的用户名和邮箱之后才会消失:

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

radio button

这里没有什么特别的地方,不过可以搭配 ngFor 让整个实现变得简单一些。

假设 VM 层有一个数组包含 [male, female] 这两个数据,V 层渲染如下:

<div class="form-group" *ngFor="let gender of genders"><label for=""></label><input type="radio" name="gender" ngModel [value]="gender" />{{ gender }}
</div>

结果如下:

在这里插入图片描述

使用 @ViewChild 获取表单

这里补充另一种可以获取表单的方式,就是通过 @ViewChild 进行绑定

@ViewChild 的使用在之前的笔记提过: [Angular 基础] - 视图封装 & 局部引用 & 父子组件中内容传递

使用方法如下:

export class AppComponent {@ViewChild('f') signupForm: NgForm;suggestUserName() {const suggestedName = 'Superuser';}// onSubmit(form: NgForm) {//   console.log('submitted');//   console.log(form);// }onSubmit(f: NgForm) {console.log(this.signupForm);}
}

输出结果如下:

在这里插入图片描述

使用 @ViewChild

使用 @ViewChild 可以提供更加灵活的使用,比如说 UI 上的一个 Suggest an Username 按钮,我想要点一下这里就生成一个用户名时,就可以使用 @ViewChild 去实现。

这里会提供两种写法,根据业务需求去使用,具体使用方式在注释里:

  • VM 层

    suggestUserName() {const suggestedName = 'Superuser';// 这里必须提供所有的值,否则会报错,因此就有可能会重写所有值的问题this.signupForm.setValue({userData: {username: suggestedName,email: '',},secret: 'pet',gender: 'male',});// 这里就是打个补丁,不会重写所有的值this.signupForm.form.patchValue({userData: {username: suggestedName,},});
    }
    

    这里直接使用 setValue 去设置一个与提交格式对应的表单

  • V 层

    <button class="btn btn-default" type="button" (click)="suggestUserName()">Suggest an Username
    </button>
    

    这里绑定 click 事件

这里的效果是重写所有值,动图如下:

在这里插入图片描述

⚠️:还是可以使用 event handler+2-way data binding 去实现的,只是对于 Template-Driven Form 来说,这种实现方法最方便

使用表单数据

这里还是需要创建一个新的对象用来 match 提交的表单数据,从而完成渲染,实现如下:

  • V 层

    <div class="row" *ngIf="f.submitted"><div class="col-xs-12"><h3>Your Data</h3><p>Username: {{ user.username }}</p><p>Email: {{ user.email }}</p><p>Secret Question: {{ user.secret }}</p><p>Gender: {{ user.gender }}</p></div>
    </div>
    
  • VM 层

    onSubmit(f: NgForm) {this.user.username = f.value.userData.username;this.user.email = f.value.userData.email;this.user.secret = f.value.secret;this.user.gender = f.value.gender;
    }
    

渲染如下:

获取数据进行渲染

这里的逻辑是只有在提交的时候会对数据进行赋值,所以直接检查 f.submitted 也不会造成修改表单数据就让下面的数据区域重新渲染的结果。

重置表单

这里就一行代码:

this.signupForm.reset();

效果如下:

在这里插入图片描述

⚠️:reset 中也可以传值,用法和 setValue 类似

reference

  • Validators

    angular 的所有 validators

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/527796.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

vue学习笔记22-组件传递多种数据类型props效验

组件传递多种数据类型 通过props传递数据&#xff0c;不仅可以传递字符串类型的数据&#xff0c;还可以是其他类型&#xff0c;例如&#xff1a;数字、对象、数组等&#xff0c;但实际上任何类型的值都可以作为props的值被传递&#xff08;即组件与组件之间的传递是没有限制的…

MySQL-----存储过程

▶ 介绍 存储过程是事先经过编译并存储在数据库中的一段SQL语句的集合&#xff0c;调用存储过程可以简化应用开发人员的很多工作&#xff0c;减少数据在数据库和应用服务器之间的传输&#xff0c;对于提高数据处理的效率是有好处的。 存储过程思想上很简单&#xff0c;…

[金三银四] 系统调用相关

2.36 系统调用的详细流程 Linux 在x86上的系统调用通过 int 0x80 实现&#xff0c;用系统调用号来区分入口函数。操作系统实现系统调用的基本过程是&#xff1a; 应用程序调用库函数&#xff08;API&#xff09;&#xff1b;API 将系统调用号存入寄存器&#xff08;EAX&#…

Git分支管理(Git分支的原理、创建、切换、合并、删除分支)

系列文章目录 文章一&#xff1a;Git基本操作 文章目录 系列文章目录前言一、Git分支是什么二、Git分支的原理三、创建分支四、切换分支五、合并分支六、删除分支 前言 在上一篇文章中&#xff0c;我们学习了如何使用Git的一些基本操作&#xff0c;例如安装Git、创建本地仓库…

Redis安全加固策略:绑定Redis监听的IP地址 修改默认端口 禁用或者重命名高危命令

Redis安全加固策略&#xff1a;绑定Redis监听的IP地址 & 修改默认端口 & 禁用或者重命名高危命令 1.1 绑定Redis监听的IP地址1.2 修改默认端口1.3 禁用或者重命名高危命令1.4 附&#xff1a;redis配置文件详解&#xff08;来源于网络&#xff09; &#x1f496;The Beg…

靶场:sql-less-18——sqlmap爆库的操作

本文操作环境&#xff1a;KaLi-Linux 靶场链接&#xff1a;Less-18 Header Injection- Error Based- string 1、打开靶场&#xff0c;挂好代理&#xff0c;使用bp抓包 2、复制抓包的数据内容&#xff0c;在kali-Linux中新建文档复制保存 3、打开命令窗口&#xff1a;确定注入点…

ElasticSearch之排序,fielddata和docvalue

写在前面 es搜索返回结果的排序默认是按照得分的高低来排的&#xff0c;本文来看下如何按照字段来排序&#xff0c;实现类似于MySQL的order by xxx的效果。 1&#xff1a;什么是fileddata和doc_value 参考ElasticSearch之零碎知识点 和一文带你彻底弄懂ES中的doc_values和fi…

25 使用块的网络 VGG【李沐动手学深度学习v2课程笔记】

目录 1. VGG块 2. VGG网络 3. 训练模型 4. 小结 虽然AlexNet证明深层神经网络卓有成效&#xff0c;但它没有提供一个通用的模板来指导后续的研究人员设计新的网络。 与芯片设计中工程师从放置晶体管到逻辑元件再到逻辑块的过程类似&#xff0c;神经网络架构的设计也逐渐变得…

python 网络库集锦

目录 通用网络库 网络爬虫框架 1.功能齐全的爬虫 2.其他 HTML/XML解析器 1.通用 2.清理 文本处理 自然语言处理 浏览器自动化与仿真 多重处理 异步网络编程库 队列 云计算 网页内容提取 WebSocket DNS解析 计算机视觉 通用网络库 1.urllib -网络库(stdlib)。…

深度学习armv8/armv9 cache的原理

文章目录 1、为什么要用cache?2、背景:架构的变化?2、cache的层级关系 ––big.LITTLE架构&#xff08;A53为例)3、cache的层级关系 –-- DynamIQ架构&#xff08;A76为例)4、DSU / L3 cache5、L1/L2/L3 cache都是多大呢6、cache相关的术语介绍7、cache的分配策略(alocation,…

通信-CAN-00 标准概述

总结了下CAN的基本知识&#xff0c;实际CAN的标准&#xff0c;内容&#xff0c;工具使用&#xff0c;上位机开发&#xff0c;下位机开发等&#xff0c;后续会找时间慢慢更新。本文主要介绍CAN标准&#xff0c;并对11898进行了进一步的介绍。 1 CAN概念 CAN-Controller Area N…

网站维护3年15000元,贵不贵?市场价多少

一般来说&#xff0c;给公司做好网站上线之后&#xff0c;网站就进入了运维期间&#xff0c;某功力公司给客户收费3年15000元网站运维费用&#xff0c;到底高不高呢&#xff1f; 首先&#xff0c;来看看网站运维都有哪些项目 网站运维涉及多个项目和任务&#xff0c;包括但不限…