本文总结了 AngularJS 使用的时候容易忽略的一些坑点。适合学完 Angular 基础并且有一定事件经验的工程师阅读,喜欢的话收藏起来吧,这是一个系列文章,大概在 5 篇左右。
40. 指令
在介绍指令之前,先写一个 css 样式,这个样式覆盖浏览器 input 元素的默认样式,使其被选中的时候不显示边框。
css
input:focus {
outline: none;
}
input 表单元素的指令
不同于其它元素上的指令,表单元素上的指令可以通过在构造函数注入ngControl依赖的方式获取其对应的 FormControl 实例。
js
import { Directive, ElementRef } from '@angular/core';
import { NgControl } from '@angular/forms';
@Directive({
selector:'[appAnswerHighlight]'
})
export class AnswerHighlightDirective {
constructor(private el: ElementRef, private controlName: NgControl) {
this.controlName.control.parent.valueChanges.subscribe(value => {
console.log(value);
});
}
}
html
<form [formGroup]="mathForm">
<div class="equation">{{ a }} + {{ b }} =</div>
<input appAnswerHighlight formControlName="answer" />
</form>
<div class="stats">{{ secondsPerSolution | number:'1.1-3' }}</div>
之前讲了对表单元素状态改变的监听,上述代码中则展示了如何监听表单元素值的改变。
41. cookie 鉴权原理
Cookie 鉴权策略是一种常见的身份验证和授权方式,广泛应用于各类网站和应用程序中。以下是 Cookie 鉴权策略的简要叙述:
- Cookie 生成与存储:当用户首次访问网站时,服务器会生成一个唯一标识符(Cookie),并通过 HTTP 响应头中的 Set-Cookie 字段发送给用户的浏览器。浏览器接收到 Cookie 后,会将其保存在本地。
- Cookie 发送与验证:在后续的访问中,浏览器会自动将之前保存的 Cookie 附加到 HTTP 请求头中发送给服务器。服务器接收到带有 Cookie 的请求后,会根据 Cookie 中的信息进行身份验证和授权操作。
- 优势:Cookie 鉴权机制简单易用,用户无需在每次访问时都输入用户名和密码;同时,Cookie 可以跨页面和跨设备传递,保持用户的登录状态。
- 安全性与隐私问题:尽管 Cookie 鉴权机制具有诸多优势,但也存在安全性和隐私问题,如 Cookie 被窃取、篡改或过期等。因此,需要采取加密、设置安全标志等措施来提高安全性。
42. 同步表单校验
先睹为快
js
import { Component, OnInit } from '@angular/core';
import { FormGroup, FormControl, Validators } from '@angular/forms';
@Component({
selector: 'app-signup',
templateUrl: './signup.component.html',
styleUrls: ['./signup.component.css']
})
export class SignupComponent implements OnInit {
authForm = new FormGroup({
username: new FormControl('', [
Validators.required,
Validators.minLength(3),
Validators.maxLength(20),
Validators.pattern(/^[a-z0-9]+$/)
]),
password: new FormControl(''),
passwordConfirmation: new FormControl('')
});
constructor() { }
ngOnInit() { }
}
自定义校验规则
除了使用类的静态方法作为校验准则,我们还可以用类的实例,这种情况下其本质就是实现了 Validator
接口的一个类。
js
import { Validator, AbstractControl, ValidationErrors, NG_VALIDATORS } from '@angular/forms';
import { Directive, Input } from '@angular/core';
// 自定义验证器类
export class CustomValidator implements Validator {
validate(control: AbstractControl): ValidationErrors | null {
// 验证逻辑:例如,检查控件的值是否为特定的字符串
const isInvalid = control.value === 'invalidValue';
return isInvalid ? { 'customError': true } : null;
}
}
注意,这里有一点可能与直觉相反,那就是如果我们校验成功,则返回 null,否则返回一个 Object 对象。这里就是用了我自己悟出来的根据返回之类型的不同来表示不同的可能性的问题。
方法或者步骤总结:
根据您提供的信息,以下是一个整理后的关于如何创建基于类的自定义同步验证器的说明:
- 创建一个新类: 创建一个新的类来实现您的自定义验证器逻辑。
- [可选] 实现 "Validator" 接口 : 让这个类实现Angular的
Validator
接口。这不是强制性的,但有助于确保您的验证器遵循预期的格式。 - 添加 "validate" 方法 : 在类中添加一个
validate
方法。这个方法将被传入一个FormGroup
或FormControl
实例,用于执行验证逻辑。 - 实现验证逻辑 : 在
validate
方法中编写自定义的验证逻辑。如果数据有效,方法应该返回null
。如果数据无效,方法应该返回一个对象,该对象描述了错误。
将校验规则作为依赖注入
在 Angular 中,如果我们需要在一个类中使用另外一个类的实例,那么我们的第一反应就应该是使用其强大的注入系统。因此,既然我们的校验器现在由实例上的方法提供,那么自然而然的,我们就应该使用注入的方式。
在 Angular 中,将一个类变成注入非常的简单,只需要使用 @Injectable
对其进行装饰即可,如下所示:
js
import { Validator, AbstractControl, ValidationErrors, NG_VALIDATORS } from '@angular/forms';
import { Directive, Input } from '@angular/core';
// 自定义验证器类
@Injectable({
providedIn: 'root'
})
export class CustomValidator implements Validator {
validate(control: AbstractControl): ValidationErrors | null {
// 验证逻辑:例如,检查控件的值是否为特定的字符串
const isInvalid = control.value === 'invalidValue';
return isInvalid ? { 'customError': true } : null;
}
}
将校验规则进化成指令
对于 input 表单,如果想要表示其必填,则可以写成这样: <input type="password" required />
我们通过一个 required 而不是在 FormModule 上设置校验规则。那么我们自定义的校验规则能否事先相同的功能呢?我们只需要在上面的代码上多加几行即可定义一个校验指令出来:
js
// 如果你希望将这个验证器作为指令使用,你可以创建一个指令
@Directive({
selector: '[appCustomValidator]',
providers: [{ provide: NG_VALIDATORS, useExisting: CustomValidatorDirective, multi: true }]
})
export class CustomValidatorDirective extends CustomValidator {}
然后就可以像使用 required 一样使用它了:
html
<input type="text" formControlName="myFormControl" appCustomValidator>
<form [formGroup]="myFormGroup">
<input type="text" formControlName="myControlName" appCustomValidator>
</form>
完全体校验类
我们的完全体校验类可以写成如下形式:
js
import { Validator, AbstractControl, ValidationErrors, NG_VALIDATORS } from '@angular/forms';
import { Directive, Input } from '@angular/core';
@Injectable({
providedIn: 'root'
})
export class CustomValidator implements Validator {
validate(control: AbstractControl): ValidationErrors | null {
const isInvalid = control.value === 'invalidValue';
return isInvalid ? { 'customError': true } : null;
}
}
@Directive({
selector: '[appCustomValidator]',
providers: [{ provide: NG_VALIDATORS, useExisting: CustomValidatorDirective, multi: true }]
})
export class CustomValidatorDirective extends CustomValidator {}
43. 异步表单校验
尽管很少见,但是在制作异步校验函数的时候,我们还是有可能直接使用 HttpClient 的实例而不是包装其的服务的。
同步、异步校验的规则
由于异步校验相对而言比较消耗性能,所以 Angular 已经内置了如下策略:有限进行同步校验,如果通过,才进一步进行异步校验 。不仅如此:Angular 中还会对异步校验进行自动的节流,反应在浏览器上就是会可能取消已经发出的网络请求。
构建一个异步校验类的步骤
- 创建一个新类: 创建一个新的类来实现您的自定义异步验证器逻辑。
- [可选] 实现"AsyncValidator"接口 : 让这个类实现Angular的
AsyncValidator
接口。这不是强制性的,但有助于确保您的验证器遵循预期的异步格式。 - 添加"validate"方法 : 在类中添加一个
validate
方法。这个方法将被传入一个FormGroup
或FormControl
实例,并应返回一个Observable
,用于执行异步验证逻辑。 - 实现异步验证逻辑 : 在
validate
方法中编写自定义的异步验证逻辑。如果数据有效,Observable
应该发出null
。如果数据无效,Observable
应该发出一个对象,该对象描述了错误。
以下是一个简单的示例代码,展示了如何实现一个基于类的自定义异步验证器:
typescript
import { AsyncValidator, AbstractControl, ValidationErrors, Observable } from '@angular/forms';
import { of } from 'rxjs'; // 引入RxJS的of函数来创建Observable
export class CustomAsyncValidator implements AsyncValidator {
validate(control: AbstractControl): Observable<ValidationErrors | null> {
// 在这里编写自定义的异步验证逻辑
// 如果验证通过,使用of(null)来返回一个发出null的Observable
// 如果验证失败,使用of({ 'customAsyncError': true })来返回一个发出错误对象的Observable
return of(/* 你的异步验证逻辑,返回null或错误对象 */);
}
}
校验用户名是否重复的校验方法
下面这段代码完整展示了一个异步的表单校验方法。
js
import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { AsyncValidator, AbstractControl, ValidationErrors, Observable } from '@angular/forms';
import { map, catchError } from 'rxjs/operators';
import { of } from 'rxjs';
@Injectable({
providedIn: 'root'
})
export class UniqueUsernameValidator implements AsyncValidator {
constructor(private http: HttpClient) {}
validate(control: AbstractControl): Observable<ValidationErrors | null> {
const { value } = control;
return this.http.post<any>(
'https://api.angular-email.com/auth/username',
{ username: value },
).pipe(
map(response => {
if (response.available) {
return null; // 如果用户名可用,则返回null表示没有错误
} else {
return { uniqueUsername: false }; // 如果用户名不可用,则返回错误对象
}
}),
catchError(err => {
console.log(err); // 打印错误信息
if (err.error && err.error.username) {
return of({ nonUniqueUsername: true }); // 如果错误中包含username字段,则返回nonUniqueUsername错误
} else {
return of({ noConnection: true }); // 否则,返回noConnection错误
}
})
);
}
}
利用上面的这段代码,我们注意到:网络请求是可能会失败的,所以在整个数据链路中我们必须手动的处理错误,防止网络错误外溢导致程序崩溃,这一点体现在 Observable 处理联调的 catchError 上,但却是和框架无关的,即在 Vue 和 React 或者原生中,也需要采用相同的策略。