今天要来用 Reactive Forms 的方式实作一个简单的登入系统,撇开 UI 不谈,具体的功能需求规格跟昨天差不多,如下所示:
/^\b[\w\.-]+@[\w\.-]+\.\w{2,4}\b$/gi
来检验,验证有误时需在栏位後方显示错误讯息:格式有误,请重新输入
此栏位必填
密码长度最短不得低於8码
密码长度最长不得超过16码
此栏位必填
规格需求看清楚之後,我们就来开始实作吧!
实作时大家可以自己开一个专案来练习,抑或是用 Stackblitz 开一个 Angular 的专案来练习,我就不再赘述罗!
如果正在阅读此篇文章的你还不知道要怎麽开始一个 Angular 专案的话,请先阅读我的 Angular 深入浅出三十天後再来阅读此系列文章会比较恰当噢!
首先我们先准备好基本的 HTML :
<form>
<p>
<label for="account">帐号:</label>
<input type="email" id="account">
</p>
<p>
<label for="password">密码:</label>
<input type="password" id="password">
</p>
<p>
<button type="submit">登入</button>
</p>
</form>
未经美化的画面应该会长这样:
接着到 app.module.ts
里 import FormsModule
与 ReactiveFormsModule
:
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import { AppComponent } from './app.component';
@NgModule({
imports: [
BrowserModule,
FormsModule,
ReactiveFormsModule
],
declarations: [AppComponent],
bootstrap: [AppComponent]
})
export class AppModule { }
然後将要绑在 Template 的属性跟方法都准备好:
export class LoginComponent implements OnInit {
// 绑定在表单上
formGroup: FormGroup;
/**
* 用以取得帐号栏位的表单控制项
*/
get accountControl(): FormControl {
return this.formGroup.get('account') as FormControl;
}
/**
* 用以取得密码栏位的表单控制项
*/
get passwordControl(): FormControl {
return this.formGroup.get('password') as FormControl;
}
/**
* 透过 DI 取得 FromBuilder 物件,用以建立表单
*/
constructor(private formBuilder: FormBuilder) {}
/**
* 当 Component 初始化的时候初始化表单
*/
ngOnInit(): void {
this.formGroup = this.formBuilder.group({
account: [
'',
[
Validators.required,
Validators.pattern(/^\b[\w\.-]+@[\w\.-]+\.\w{2,4}\b$/gi)
]
],
password: [
'',
[
Validators.required,
Validators.minLength(8),
Validators.maxLength(16)
]
]
});
}
// 绑定在表单上,当使用者按下登入按钮时会触发此函式
login(): void {
// do login...
}
/**
* 透过该栏位的表单控制项来取得该栏位的错误讯息
*
* @param {FormControl} formControl 欲取得错误讯息的栏位的表单控制项 (by Angular)
*/
getErrorMessage(formControl: FormControl): string {
let errorMessage: string;
if (!formControl.errors || formControl.pristine) {
errorMessage = '';
} else if (formControl.errors.required) {
errorMessage = '此栏位必填';
} else if (formControl.errors.pattern) {
errorMessage = '格式有误,请重新输入';
} else if (formControl.errors.minlength) {
errorMessage = '密码长度最短不得低於8码';
} else if (formControl.errors.maxlength) {
errorMessage = '密码长度最长不得超过16码';
}
return errorMessage;
}
}
就可以将这些属性和方法跟 Template 绑定在一起:
<form [formGroup]="formGroup" (ngSubmit)="login()">
<p>
<label for="account">帐号:</label>
<input
type="email"
id="account"
[formControl]="accountControl"
/>
<span class="error-message">{{ getErrorMessage(accountControl) }}</span>
</p>
<p>
<label for="password">密码:</label>
<input
type="password"
id="password"
[formControl]="passwordControl"
/>
<span class="error-message">{{ getErrorMessage(passwordControl) }}</span>
</p>
<p>
<button type="submit" [disabled]="formGroup.invalid">登入</button>
</p>
</form>
到目前为止的程序码你看懂了多少呢?对於刚接触 Angular 的表单的朋友来说,今天的资讯量可能会比较大,容我稍微说明一下:
Reactive Forms 的概念是将表单用程序的方式产生。以这个需求来说,这个表单底下会有两个栏位 account
与 password
,如果将其用 JSON
来表示的话,应该会长这样:
{
"account": "",
"password": ""
}
从资料面来看, {}
代表表单, "account": ""
与 "password": ""
则是里面的两个栏位。
而再将其转换成 Reactive Forms 的概念的话, {}
代表的是 FormGroup
,"account": ""
与 "password": ""
则代表的是 FormControl
。
所以在程序码中我们可以看到我们宣告 formGroup: FromGroup;
并且在 template 中将其绑定在表单上:
<form [formGroup]="formGroup">
<!-- ... -->
</form>
并且把表单控制项绑定在对应的 input 栏位上:
<!-- 帐号栏位 -->
<input
type="email"
id="account"
[formControl]="accountControl"
/>
<!-- 密码栏位 -->
<input
type="password"
id="password"
[formControl]="passwordControl"
/>
然後在 ngOnInit
里透过 FormBuilder
来初始化表单:
ngOnInit(): void {
this.formGroup = this.formBuilder.group({
account: '我是该栏位的初始值',
password: '我是该栏位的初始值'
});
}
如此一来,就可以在初始化过後,跟我们的 template 正确绑定了。
而如果当该栏位需要验证时,就要在初始化时将格式调整成:
ngOnInit(): void {
this.formGroup = this.formBuilder.group({
account: ['我是该栏位的初始值', /* 验证器的摆放位置 */],
password: ['我是该栏位的初始值', /* 验证器的摆放位置 */],
});
}
如果只有一个要验证的项目则可以直接放入:
ngOnInit(): void {
this.formGroup = this.formBuilder.group({
account: ['我是该栏位的初始值', Validators.required],
password: ['我是该栏位的初始值', Validators.required],
});
}
如果有多个要验证的项目,就用 []
将多个验证项包起来再放入:
ngOnInit(): void {
this.formGroup = this.formBuilder.group({
account: [
'我是该栏位的初始值',
[
Validators.required,
Validators.pattern(/^\b[\w\.-]+@[\w\.-]+\.\w{2,4}\b$/gi)
]
],
password: [
'我是该栏位的初始值',
[
Validators.required,
Validators.minLength(8),
Validators.maxLength(16)
]
],
});
}
在这里我们可以发现,上一篇使用 Template Driven Forms 实作时,是用 HTML 原生的属性来验证,而今天使用 Reactive Forms 实作时,则是用程序来验证,如此一来,可以降低表单与 template 之间的依赖性,使得其更易於维护、重用与测试。
Validators 是 Angular 帮我们制作的验证器,里面有很多常用验证器,详细请参考官方文件
当然我们也可以自己客制验证器,只要符合 ValidatorFn 的类型即可
关於错误讯息基本上可以沿用上一篇的程序,只不过原本是传入 FormControl
的 errors
来判断,但现在是传入整个 FormControl
,为什麽呢?
因为如果只有传入 FormControl
的 errors
的话,你会发现表单初始化完之後,就会有错误讯息显示在画面上:
这是因为当我们的表单初始化完之後,验证器就会开始运作,所以的确那个两个栏位是有那个错误没错,但其实这不是我们想要的行为,因为使用者根本就还没有开始填表单,我们想要的是当使用者开始填表单之後,才会显示对应的错误讯息,所以我们改传入整个 FormControl
,它其中有几个很好用的属性可以使用:
pristine
─ 如果此属性为 true
,代表该栏位是乾净,没有被输入过值;反之则代表有被输入过值,与 dirty
成反比。touched
─ 如果此属性为 true
,代表该栏位曾经被碰(该栏位曾经被使用滑鼠 focus 过);反之则代表该栏位完全没被碰过。dirty
─ 如果此属性为 true
,代表该栏位曾经被输入过值,已经脏掉了;反之则代表该栏位是乾净,没有被输入过值,与 pristine
成反比。想知道更多可以参考官方文件: FormControl 与其抽象类别 AbstractControl
所以我们只要加上当该栏位是乾净的,就不回传错误讯息的判断就可以了,像是这样:
getErrorMessage(formControl: FormControl): string {
let errorMessage: string;
if (!formControl.errors || formControl.pristine) {
errorMessage = '';
}
// 其他省略...
}
最终结果:
对於第一次接触 Reactive Forms 的朋友们,今天的资讯量会比较多,但重点大致上可归纳成以下四点:
此外,千万记得要 import FormsModule
与 ReactiveFormsModule
才可以使用噢!
我一样会将今日的实作程序码放在 Stackblitz 上供大家参考,建议大家在看我的实作之前,先按照需求规格自己做一遍,之後再跟我的对照,看看自己的实作跟我的实作不同的地方在哪里、有什麽好处与坏处,如此反覆咀嚼消化後,我相信你一定可以进步地非常快!
如果有任何的问题或是回馈,也都非常欢迎留言给我让我知道噢!
<<: Day 18:Kotlin 过滤(filter)集合资料用法
>>: [Day-18] R语言 - 分群应用(一) k - prototype类别补值 - 上 ( Fill.NA with k - prototype in R.Studio )
在资料库管理上,root 相当於拥有所有权限的最大管理者,针对不同使用者规划给予相应的权限是很重要的...
Express 利用 pm2 做管理(因为 docker 坑很深 加上来的话会写不完) Expres...
点开Project Settings的other,把Scripting Backend改为IL2CP...
就像昨天提到的,我们设定好广告活动和群组之後,当然 Google 会按照你期待的方式,将广告费用投放...
今天我们接着继续和DataFrame继续奋斗!先把套件和档案载入: import pandas as...