Angular 4.x Template-Driven Forms
Angular 4.x 中有两种表单:
Template-Driven Forms - 模板驱动式表单 (类似于 AngularJS 1.x 中的表单 )
Reactive Forms - 响应式表单
本文主要介绍 Template-Driven Forms (模板驱动式表单) ,将涉及 ngForm
、ngModel
、ngModelGroup
、表单提交事件、表单验证和异常信息输出等内容。
Contents
ngModule and template-driven forms
Binding ngForm and ngModel
ngModel,[ngModel] and [(ngModel)]
ngModels and ngModelGroup
Template-driven submit
Template-driven error validation
Form base and interface
Form base
<form novalidate><label><span>Full name</span><inputtype="text"name="name"placeholder="Your full name"></label><div><label><span>Email address</span><inputtype="email"name="email"placeholder="Your email address"></label><label><span>Confirm address</span><inputtype="email"name="confirm"placeholder="Confirm your email address"></label></div><button type="submit">Sign up</button>
</form>
接下来我们要实现的功能如下:
绑定 name、email、confirm 输入框的值
为所有输入框添加表单验证功能
显示验证异常信息
表单验证失败时,不允许进行表单提交
表单提交功能
User interface
// signup.interface.ts
export interface User {name: string;account: {email: string;confirm: string;}
}
ngModule and template-driven forms
在我们继续深入介绍 template-driven 表单前,我们必须在 @NgModule
中导入 @angular/forms
库中的 FormModule
:
import { FormsModule } from '@angular/forms';@NgModule({imports: [...,FormsModule],declarations: [...],bootstrap: [...]
})
export class AppModule {}
友情提示:若使用 template-driven 表单,则导入 FormsModule;若使用 reactive forms,则导入 ReactiveFormsModule。
Template-driven approach
使用模板驱动的表单,我们基本上可以将组件类留空,直到我们需要读取/写入值 (例如提交和设置初始值)。我们将基于上面的定义的基础表单,创建 SignupFormComponent
:
signup-form.component.ts
import { Component } from '@angular/core';@Component({selector: 'signup-form',template: `<form novalidate>...</form>`
})
export class SignupFormComponent {constructor() {}
}
这是一个很基础的组件,接下来我们导入之前定义的 User
接口,具体如下:
import { User } from './signup.interface';@Component({...})
export class SignupFormComponent {public user: User = {name: '',account: {email: '',confirm: ''}};
}
初始化 SignupFormComponent 组件类中的用户模型后,我们开始实现第一个功能点:即绑定 name、email、confirm 输入框的值。
Binding ngForm and ngModel
我们从 ngForm 开始,更新后的模板如下:
<form novalidate #f="ngForm"><label><span>Full name</span><input type="text" placeholder="Your full name"></label>
</form>
上面代码中,我们把 ngForm
的值赋值给 #f
变量,通过该变量我们可以方便的获取表单的值。
友情提示:#f 变量的值,是 ngForm 指令的导出对象。
@Directive({selector: 'form:not([ngNoForm]):not([formGroup]),ngForm,[ngForm]',providers: [formDirectiveProvider],host: {'(submit)': 'onSubmit($event)', '(reset)': 'onReset()'},outputs: ['ngSubmit'],exportAs: 'ngForm'
})
export class NgForm extends ControlContainer implements Form {}
在模板中,我们可以通过以下方式查看表单的值:
{{ f.value | json }} // {}
上面示例 f.value
输出 {},因为此时我们表单中还未绑定任何值。在 Angular 1.x 中我们可以使用 ng-model
指令进行表单数据的双向绑定,接下来我们来看一下 Angular 4.x 中怎么实现数据绑定。
ngModel,[ngModel] and [(ngModel)]
在 Angular 4.x 中 ngModel
有三种不同的语法:
ngModel - 直接使用 ngModel 指令,没有使用绑定或关联任何值。此时,ngModel 将自动关联表单控件的 name 属性,并使用该值作为 ngForm 对象的属性名。
<form novalidate #f="ngForm">...<inputtype="text"placeholder="Your full name"name="name"ngModel>...
</form>
友情提示:上面示例中,如果 input 输入框若未设置 name 属性,应用将会抛出异常。ngModel 指令基于输入框的 name 属性,进行绑定。
运行以上代码,f.value 的输入值如下:
{{ f.value | json }} // { name: '' }
非常好,我们已经绑定了 name 输入框的值。但我们应该怎么为输入框设置初始值?
[ngModel] = one-way binding syntax (单向绑定语法)
为了设置输入框初始值,我们先要更新一下 SignupFormComponent 组件类的用户模型:
...
user: User = {name: 'Semlinker',account: {email: '',confirm: ''}
};
...
更新完用户模型,我们需要同步更新组件模板,具体如下:
<form #f="ngForm">...<inputtype="text"placeholder="Your full name"name="name"[ngModel]="user.name">...
</form>
代码重新运行后,f.value 的输出如下:
{{ f.value | json }} // { name: 'Semlinker' }
从上面示例可以看出,使用 [ngModel]
允许我们通过 this.user.name
设置 name 输入框的初始值,而且该值会自动绑定到 f.value
对象上。
友情提示:[ngModel] 是单向绑定,当表单中 name 输入框的值改变时,不会同步更新 this.user.name
如果想在 name 输入框值变化时,自动同步更新 this.user.name
的值,我们需要使用双向绑定。
[(ngModel)] = two-way binding syntax (双向绑定),具体示例如下:
<form #f="ngForm">...<inputtype="text"placeholder="Your full name"name="name"[(ngModel)]="user.name">...
</form>
上面示例成功运行后,我们可以在模板中新增以下代码,然后观察 user
模型的值:
{{ user | json }} // { name: 'Semlinker' }
需要注意的是,以下两种方式是等价的:
<input [(ngModel)]="user.name">
<input [ngModel]="user.name" (ngModelChange)="user.name = $event">
其中 [(ngModel)]
是简写的语法糖。
ngModels and ngModelGroup
我们已经介绍了 ngForm
和 ngModel
的基础用法,现在我们来完善剩下的内容。SignupFormComponent 组件类的用户模型中,包含了一个嵌套属性 account
,account 对象中包含 email
和 confirm
属性,分为表示邮件地址和重复确认的邮件地址。针对这种场景,Angular 4.x 为我们提供了 ngModelGroup
指令,具体示例如下:
<form novalidate #f="ngForm"><label><span>Full name</span><inputtype="text"placeholder="Your full name"name="name"ngModel></label><div ngModelGroup="account"><label><span>Email address</span><inputtype="email"placeholder="Your email address"name="email"ngModel></label><label><span>Confirm address</span><inputtype="email"placeholder="Confirm your email address"name="confirm"ngModel></label></div><button type="submit">Sign up</button>
</form>
使用 ngModelGroup
指令后,我们的 DOM 结构将更加合理:
ngForm -> '#f'ngModel -> 'name'ngModelGroup -> 'account'-> ngModel -> 'email'-> ngModel -> 'confirm'
以上代码成功运行后,浏览器中页面显示的结果:
// { name: 'Semlinker', account: { email: '', confirm: '' } }
{{ f.value | json }}
此时我们已经完成了表单数据绑定,接下来我们来为表单增加提交逻辑。
Template-driven submit
Angular 表单中提供了 ngSubmit
输出属性,用于监听表单的提交事件:
<form novalidate (ngSubmit)="onSubmit(f)" #f="ngForm">...
</form>
当用户提交表单时,我们将会把 f
作为参数,调用 ngSubmit
关联的 onSubmit()
方法。onSubmit() 方法的具体实现如下:
export class SignupFormComponent {user: User = {...};onSubmit({ value, valid }: { value: User, valid: boolean }) {console.log(value, valid);}
}
上面代码中,我们使用 Object destructuring
(对象解构) 的方式,从#f
引用对象中获取 value
和 valid
属性的值。其中 value
的值,就是 f.value
的值。表单的数据绑定方式和提交逻辑已经介绍完了,是该介绍表单实际应用中,一个重要的环节 — 表单验证。
Template-driven error validation
在为表单项添加验证规则前,我们先来更新一下 SignupFormComponent 组件中的 Sign up
按钮,确保在表单验证不通过时,不允许用户执行表单提交操作。更新后的代码如下:
<form novalidate (ngSubmit)="onSubmit(f)" #f="ngForm">...<button type="submit" [disabled]="f.invalid">Sign up</button>
</form>
以上代码我们通过 f.invalid
获取表单当前的验证状态 (验证不通过时该值为true),来控制按钮的 disabled
属性。
接下来开始进入正题,为表单添加验证规则:
<form novalidate #f="ngForm"><label>...<input...ngModelrequired></label><div ngModelGroup="account"><label>...<input...name="email"ngModelrequired></label><label>...<input...name="confirm"ngModelrequired></label></div><button type="submit">Sign up</button>
</form>
上面代码中,我们为每个 input 表单控件,添加了 required
(必填项) 的验证规则。一切都那么简单,剩下的问题就是如何获取验证失败的异常消息。
皇上,您还记得当年大明湖畔的夏雨荷吗? — No,No,No !我只记得安谷拉 (angular) 湖畔的美女 (f)。
#f
引用对象中有一个 controls
属性,通过该属性,我们就可以获取表单控件的验证信息,下面示例演示了如何获取 name 表单控件验证的异常信息:
<form novalidate #f="ngForm">{{ f.controls.name?.errors | json }}
</form>
f.controls.name?.errors
的值是 null
或 undefined
时,表示验证成功。
友情提示:?.prop 称为安全导航操作符,用于告诉 Angular prop 的值可能不存在。
接下来为我们的 name 表单控件,添加显示异常信息的代码:
<div *ngIf="f.controls.name?.required" class="error">Name is required
</div>
虽然我们已经可以获取某个表单项的验证信息,但有没有觉得使用 f.controls.name?.errors
这种方式,太麻烦了。那么有没有更简单的方式呢?我的答案是 - Yes !废话不多说,马上看示例:
<label>...<input...#userName="ngModel"required>
</label>
<div *ngIf="userName.errors?.required" class="error">Name is required
</div>
(备注:此处一定要使用 #userName="ngModel"
)
以上代码成功运行后,我们在浏览器中看到了异常信息,为了避免一开始就显示异常信息,我们可以更新一下 *ngIf
表达式的验证逻辑:
<div *ngIf="userName.errors?.required && userName.touched" class="error">Name is required
</div>
除了使用 required
验证规则之外,我们还可以使用 minlength
(最小长度)、maxlength
(最大长度) 等验证规则,下面我们继续来完善 SignupFormComponent 组件的功能,即为其它的表单控件添加显示异常信息的功能:
<!-- name -->
<div *ngIf="userName.errors?.required && userName.touched" class="error">Name is required
</div>
<div *ngIf="userName.errors?.minlength && userName.touched" class="error">Minimum of 2 characters
</div><!-- account: { email, confirm } -->
<div *ngIf="userEmail.errors?.required && userEmail.touched" class="error">Email is required
</div>
<div *ngIf="userConfirm.errors?.required && userConfirm.touched" class="error">Confirming email is required
</div>
我们通过使用模板变量的方式,为 account 表单组添加了显示验证异常信息的功能。但有没有其它更好的方式呢?有没有办法去掉 userEmail
和 userConfirm
引用对象呢?答案是肯定的,具体示例如下:
<div ngModelGroup="account" #userAccount="ngModelGroup"><label><span>Email address</span><inputtype="email"placeholder="Your email address"name="email"ngModelrequired></label><label><span>Confirm address</span><inputtype="email"placeholder="Confirm your email address"name="confirm"ngModelrequired></label><div *ngIf="userAccount.invalid && userAccount.touched" class="error">Both emails are required</div>
</div>
(备注:记得移除模板上的 #userEmail
和 #userConfirm
引用哈)
我有话说
表单控件的状态除了 touched
外,还包含其它几种状态?
表单控件有以下 6 种状态,我们可以通过 #userName="ngModel"
方式获取对应的状态值。具体状态如下:
valid - 表单控件有效
invalid - 表单控件无效
pristine - 表单控件值未改变
dirty - 表单控件值已改变
touched - 表单控件已被访问过
untouched - 表单控件未被访问过
表单控件上 #userName
和 #userName="ngModel"
这两种方式有什么区别?
#userName
- 指向 input 表单控件#userName="ngModel"
- 指向 NgModel 实例
ngModel 指令
// angular2/packages/forms/src/directives/ng_model.ts 片段
@Directive({selector: '[ngModel]:not([formControlName]):not([formControl])',providers: [formControlBinding],exportAs: 'ngModel' // // 导出指令实例,使得可以在模板中调用
})
export class NgModel extends NgControl implements OnChanges, OnDestroy {
}
NgControl 抽象类
// angular2/packages/forms/src/directives/ng_control.ts 片段
export abstract class NgControl extends AbstractControlDirective {/** @internal */_parent: ControlContainer = null;name: string = null;valueAccessor: ControlValueAccessor = null;...abstract viewToModelUpdate(newValue: any): void;
}
AbstractControlDirective 抽象类
// angular2/packages/forms/src/directives/abstract_control_directive.ts 片段
export abstract class AbstractControlDirective {get valid(): boolean { return this.control ? this.control.valid : null; }get invalid(): boolean { return this.control ? this.control.invalid : null; }get errors(): ValidationErrors | null { return this.control ? this.control.errors : null; }get pristine(): boolean { return this.control ? this.control.pristine : null; }get dirty(): boolean { return this.control ? this.control.dirty : null; }get touched(): boolean { return this.control ? this.control.touched : null; }get untouched(): boolean { return this.control ? this.control.untouched : null; }get valueChanges(): Observable<any> { return this.control ? this.control.valueChanges : null; }hasError(errorCode: string, path: string[] = null): boolean {return this.control ? this.control.hasError(errorCode, path) : false;}getError(errorCode: string, path: string[] = null): any {return this.control ? this.control.getError(errorCode, path) : null;}
}
ngModelGroup 有什么作用?
ngModelGroup 指令是 Angular 提供的另一特殊指令,可以对表单输入内容进行分组,方便我们在语义上区分不同性质的输入。例如联系人的信息包括姓名及住址,现在需对姓名和住址进行精细化信息收集,姓名可精细化成姓和名字,地址可精细化成城市、区、街等。此时就可以将姓名及住址进行分组收集,具体如下:
<form #concatForm = "ngForm"><fieldset ngModelGroup="nameGroup" #nameGroup="ngModelGroup"><label>姓:</label><input type="text" name="firstname" [(ngModel)]="curContact.firstname" required> <label>名字:</label><input type="text" name="lastname" [(ngModel)]="curContact.lastname" required></fieldset><fieldset ngModelGroup="addressGroup" #addressGroup ="ngModelGroup"><label>街:</label><input type="text" name="street" [(ngModel)]="curContact.street" required> <label>区:</label><input type="text" name="zip" [(ngModel)]="curContact.zip" required> <label>城市:</label><input type="text" name="city" [(ngModel)]="curContact.city" required></fieldset>
</form>
上述例子分别对联系人的姓名和住址进行分组, ngModelGroup 将姓和名字的表单内容进行包裹组成姓名分组,将城市、区和街道的表单内容进行包裹组成住址分组。此时concatForm.value值为:
{nameGroup: {firstname: '',lastname: '',},addressGroup: { street: '', zip: '', city: ''}
}
参考资源
Angular 2 form fundamentals: template-driven forms
Angular 4.x Template-Driven Forms相关推荐
- 【译】Angular 5 新特性
原文 Build Optimizer (构建优化) 从5.0.0开始,使用CLI创建的生产构建将默认应用于构建优化器. 构建优化器是我们的CLI中包含的一个工具,可以使用我们对Angular应用程序的 ...
- Angular form 官网文档的学习笔记:Angular两种实现form的方式
https://angular.io/guide/forms-overview Angular提供了两种通过form来处理用户输入的方式: reactive form template-driven ...
- Angular 4.x 自定义验证指令
表单是几乎每个 Web 应用程序的一部分.虽然 Angular 为我们提供了几个内置 validators (验证器),但在实际工作中为了满足项目需求,我们经常需要为应用添加一些自定义验证功能.接下来 ...
- ES6, Angular,React和ABAP中的String Template(字符串模板)
String Template(字符串模板)在很多编程语言和框架中都支持,是一个很有用的特性.本文将Jerry工作中使用到的String Template的特性做一个总结. ES6 阮一峰老师有一个专 ...
- Angular 2+ Material Design Admin Template
发现一个好网站:https://www.22vd.com/en/?post_type=post&s=Angular Portal – Angular, React & HTML Mat ...
- APEX V2.0 - ANGULAR 5+ BOOTSTRAP 4 HTML ADMIN TEMPLATE启动报错解决办法
报错信息: ERROR in ./node_modules/ng2-smart-table/components/cell/cell.module.js Module not found: Error ...
- svg操纵方案 基于 D3 还是 angular?
之前还是想简单了, 现在重新写这篇.把逻辑拆分粒度的辨析,放到外面去. 问题提出:svg控制方案 基于 D3 还是 angular 根据这个,html 4种展现样式:普通的html,svg,2D ca ...
- Angular 4.x 自定义表单控件
当我们打算自定义表单控件前,我们应该先考虑一下以下问题: 是否已经有相同语义的 native (本机) 元素?如:<input type="number"> 如果有,我 ...
- pb怎么封装com组件_从零开始构建 Angular 组件库
NG-ZORRO 组件库官网地址:Ant Design Of Angular Github地址:NG-ZORRO/ng-zorro-antd 更新:视频已上传 谢亚东演讲视频_腾讯视频v.qq.co ...
- angular 定义对象_angular – 使用对象定义定义FomGroup
我认为你在寻找的是Angular中的 Dynamic Forms. 根据Angular指南: Building handcrafted forms can be costly and time-con ...
最新文章
- Java过滤器与SpringMVC拦截器之间的关系与区别
- python中的encode()和decode()函数
- 多个网站tracert测试用的批处理
- 信息学奥赛一本通 1025:保留12位小数的浮点数 | OpenJudge NOI 1.1 05
- 软件测试安全性翻译成英语,中文翻译英语测试题-请大家帮我翻译一段关于软件测试的英文(译成中文)#8226; 爱问知识人...
- 大数据日知录_腾讯T4专家精心整理:java+大数据+机器学习+数据挖掘+算法大集结...
- 《Programming in Lua 3》读书笔记(五)
- 苹果修复三个已遭利用的 iOS 0day
- 云桌面 瘦终端_云终端与瘦终端简介
- 理财笔记 - 关于沪深300指数基金
- R语言gganimate的使用与呈现
- 所谓“螺旋方阵”,是指对任意给定的N,将1到N×N的数字从左上角第1个格子开始,按顺时针螺旋方向顺序填入N×N的方阵里。本题要求构造这样的螺旋方阵。
- 夜天之书 #49 开源软件的技术写作
- 马自达推出首个混合动力车型
- shell 语句出错自动退出
- canvas画正方形、三角形以及虚线
- 基于OpenDDS+Qt的Shapes演示
- Linux下RTC时钟使用
- (复习)android 调用系统相机 图库 裁剪-图片上传-客服端-服务器
- 2021-2027全球与中国数据分析服务市场现状及未来发展趋势