Reactive Forms Trong Angular (Phần 2)
- 15-01-2024
- Toanngo92
- 0 Comments
Mục lục
Điều kiện bắt buộc
Để tạo form đăng nhập, chúng ta sử dụng FormBuilder
để làm cho mã nguồn ngắn gọn hơn. Form này bao gồm hai ô văn bản cho tên người dùng và mật khẩu cùng với một ô đánh dấu.
Đầu tiên, trong component của chúng ta, chúng ta sử dụng FormBuilder
để khởi tạo form:
import { Component, OnInit } from '@angular/core';
import { FormBuilder, FormGroup } from '@angular/forms';
@Component({
selector: 'app-sign-in-rf',
templateUrl: './sign-in-rf.component.html',
styleUrls: ['./sign-in-rf.component.css']
})
export class SignInRfComponent implements OnInit {
signInForm: FormGroup;
constructor(private fb: FormBuilder) {}
ngOnInit(): void {
this.signInForm = this.fb.group({
username: '',
password: '',
rememberMe: false,
});
}
}
Sau đó, chúng ta liên kết form này với template HTML của chúng ta:
<form class="sign-in-form" [formGroup]="signInForm">
<h2>Sign in</h2>
<div class="row-control">
<mat-form-field appearance="outline">
<mat-label>Username</mat-label>
<input matInput placeholder="Username" formControlName="username" />
</mat-form-field>
</div>
<div class="row-control">
<mat-form-field appearance="outline">
<mat-label>Password</mat-label>
<input
type="password"
matInput
placeholder="Password"
formControlName="password"
/>
</mat-form-field>
</div>
<div class="row-control">
<mat-checkbox formControlName="rememberMe">Remember me</mat-checkbox>
</div>
<div class="row-control row-actions">
<button mat-raised-button color="primary" type="submit">Sign in</button>
</div>
<pre>{{ signInForm.value | json }}</pre>
</form>
Ở đây, chúng ta sử dụng [formGroup]
để liên kết form với signInForm
, và với mỗi control của form, chúng ta sử dụng directive formControlName
với giá trị là key tương ứng trong signInForm
. Chắc chắn rằng các giá trị trong formControlName
khớp chính xác với key được đặt trong signInForm
.
Yêu cầu Validate data
Trong tình huống giả định từ [ngày 34], giả sử chúng ta có một số yêu cầu cụ thể khi validate dữ liệu:
- Username Validation:
- Không được để trống.
- Độ dài từ 6 đến 32 ký tự.
- Chỉ chứa các ký tự chữ cái.
username: new FormControl('', [
Validators.required,
Validators.minLength(6),
Validators.maxLength(32),
Validators.pattern(/^[a-zA-Z]+$/),
]),
2. Password Validation:
- Không được để trống.
- Độ dài từ 6 đến 32 ký tự.
- Chứa ít nhất một ký tự chữ cái.
- Chứa ít nhất một chữ số.
- Phải chứa ít nhất một ký tự đặc biệt trong danh sách: !@#$%^&*.
password: new FormControl('', [
Validators.required,
Validators.minLength(6),
Validators.maxLength(32),
Validators.pattern(/^(?=.*[a-zA-Z])(?=.*\d)(?=.*[!@#\$%^&*])[a-zA-Z\d!@#\$%^&*]+$/),
]),
Chúng ta sử dụng FormControl
để đại diện cho mỗi trường dữ liệu, và Validators
để áp dụng các quy tắc kiểm tra. Điều này giúp đảm bảo rằng dữ liệu được người dùng nhập vào đáp ứng đúng các yêu cầu đã đề ra.
Validate Forms with Reactive Forms
Trong Reactive Forms, chúng ta thiết lập form từ component và liên kết nó với phần template HTML. Ngược lại với Template Forms, nơi chúng ta sử dụng các thuộc tính trực tiếp trên template, các validators trong Reactive Forms được định nghĩa trong quá trình thiết lập form bằng FormBuilder. Dưới đây là mô tả chi tiết:
Đầu tiên, chúng ta sẽ import FormBuilder
và Validators
từ thư viện @angular/forms
:
import { FormBuilder, Validators } from '@angular/forms';
Tiếp theo, trong class component, chúng ta sử dụng FormBuilder
để tạo form và định nghĩa các validators:
export class YourComponent implements OnInit {
myForm = this.fb.group({
// Other form controls...
username: ['', [Validators.required, Validators.minLength(6), Validators.maxLength(32), Validators.pattern(/^[a-zA-Z]+$/)]],
password: ['', [Validators.required, Validators.minLength(6), Validators.maxLength(32), Validators.pattern(/^(?=.*[a-zA-Z])(?=.*\d)(?=.*[!@#\$%^&*])[a-zA-Z\d!@#\$%^&*]+$/)]],
});
constructor(private fb: FormBuilder) {}
ngOnInit(): void {
// Additional initialization logic...
}
}
Ở đây, chúng ta sử dụng this.fb.group
để tạo một FormGroup và Validators
để áp dụng các quy tắc kiểm tra cho từng control (như username
và password
). Các validators được đặt trong một mảng và được truyền vào khi khởi tạo FormControl tương ứng.
Việc này giúp chúng ta duy trì một cách tiếp cận linh hoạt và dễ bảo trì khi quản lý validators trong Reactive Forms.
Validator functions
Có hai loại hàm kiểm tra hợp lệ (validator function) trong Angular Reactive Forms:
- Hàm kiểm tra đồng bộ (Sync Validators):
- Đây là các hàm kiểm tra phổ biến, chúng nhận một đối tượng form control làm đầu vào và trả về ngay lập tức:
- Một danh sách các lỗi kiểm tra.
- Hoặc null nếu control này không có lỗi nào.
- Ví dụ, nếu một input yêu cầu có độ dài tối thiểu là 6, hàm kiểm tra sẽ kiểm tra giá trị của control và trả về
{ "notValid": "input too short!"}
nếu không đủ độ dài, ngược lại là null. - Khi khởi tạo FormControl, hàm kiểm tra đồng bộ sẽ được truyền vào ở đối số thứ hai. Đối số thứ nhất sẽ là giá trị mặc định khi khởi tạo form.
- Đây là các hàm kiểm tra phổ biến, chúng nhận một đối tượng form control làm đầu vào và trả về ngay lập tức:
let control = new FormControl("", Validators.required);
// Hoặc
this.fb.control("", Validators.required);
2. Hàm kiểm tra bất đồng bộ (Async Validators):
- Đây là các hàm kiểm tra sẽ trả về một Promise hoặc Observable, với kết quả sẽ được phát ra trong tương lai. Ví dụ, nếu bạn muốn kiểm tra xem tên người dùng nhập vào đã tồn tại trong hệ thống chưa, bạn cần gửi một yêu cầu đến máy chủ để thực hiện công việc này.
- Khi khởi tạo FormControl, hàm kiểm tra bất đồng bộ sẽ được truyền vào ở đối số thứ ba.
- Ví dụ:
isUserNameDuplicated(control: AbstractControl): Observable<ValidationErrors> {
return of(null); // Giả sử không có lỗi
}
let control = new FormControl("", Validators.required, this.isUserNameDuplicated);
// Hoặc
this.fb.control("", Validators.required, this.isUserNameDuplicated);
Vì mục đích hiệu suất, Angular chỉ chạy các hàm kiểm tra bất đồng bộ nếu tất cả các hàm kiểm tra đồng bộ đều thành công. Mỗi hàm phải hoàn thành trước khi các lỗi được thiết lập.
Implement validate function
Angular cung cấp một bộ các hàm kiểm tra hợp lệ trong class Validators, giúp chúng ta dễ dàng thực hiện các yêu cầu kiểm tra trên form. Dưới đây là một số hàm kiểm tra phổ biến trong class Validators:
class Validators {
static min(min: number): ValidatorFn;
static max(max: number): ValidatorFn;
static required(control: AbstractControl): ValidationErrors | null;
static requiredTrue(control: AbstractControl): ValidationErrors | null;
static email(control: AbstractControl): ValidationErrors | null;
static minLength(minLength: number): ValidatorFn;
static maxLength(maxLength: number): ValidatorFn;
static pattern(pattern: string | RegExp): ValidatorFn;
static nullValidator(control: AbstractControl): ValidationErrors | null;
static compose(validators: ValidatorFn[]): ValidatorFn | null;
static composeAsync(validators: AsyncValidatorFn[]): AsyncValidatorFn | null;
}
Trong ví dụ của chúng ta, chúng ta sẽ sử dụng Validators.required, Validators.minLength và Validators.pattern để đáp ứng các yêu cầu đã mô tả. Phần mã khởi tạo form với các hàm kiểm tra sẽ có dạng như sau:
this.signInForm = this.fb.group({
username: [
"",
Validators.compose([
Validators.required,
Validators.minLength(6),
Validators.pattern(/^[a-z]{6,32}$/i),
]),
],
password: [
"",
Validators.compose([
Validators.required,
Validators.minLength(6),
Validators.pattern(/^(?=.*[!@#$%^&*]+)[a-z0-9!@#$%^&*]{6,32}$/),
]),
],
rememberMe: false,
});
Chúng ta sử dụng Validators.compose và truyền vào một mảng các hàm kiểm tra để kết hợp nhiều loại kiểm tra với nhau. Mã mẫu trên cũng hiển thị cách sử dụng các điều kiện để xác định lỗi và hiển thị trên giao diện người dùng.
Để khởi tạo FormControl trong FormGroup bằng FormBuilder, thay vì truyền ba đối số riêng lẻ, bạn có thể truyền vào một mảng có ba phần tử: giá trị mặc định, kiểm tra đồng bộ và kiểm tra bất đồng bộ.
Kết quả là, trên giao diện người dùng, các lỗi sẽ được hiển thị theo yêu cầu, và nút “Sign In” chỉ sẽ được kích hoạt nếu form hợp lệ.
Your first custom validator
Hãy chú ý rằng Validators.required chỉ kiểm tra xem input có giá trị hay không, validator này sẽ trả về null khi control là hợp lệ. Nếu bạn chỉ nhập khoảng trắng, control cũng sẽ được Validators.required chấp nhận.
Trong ví dụ trước đó, sau khi điền đủ 6 dấu cách, Validators.required và Validators.minLength đã qua được. Tuy nhiên, vì có Validators.pattern, control vẫn không hợp lệ. Ví dụ, nếu bạn muốn cho phép người dùng nhập tất cả các ký tự vào trường username mà không giới hạn bởi pattern. Do đó, ta tạm thời loại bỏ Validators.pattern(/^[a-z]{6,32}$/i).
Bây giờ, để giải quyết vấn đề trên mà không sử dụng Validators.pattern(/^[a-z]{6,32}$/i), chúng ta sẽ tạo một custom validator với tên là NoWhitespaceValidator.
import { AbstractControl, ValidatorFn } from "@angular/forms";
export function NoWhitespaceValidator(): ValidatorFn {
return (control: AbstractControl): { [key: string]: any } => {
let controlVal = control.value;
if (typeof controlVal === "number") {
controlVal = `${controlVal}`;
}
let isWhitespace = (controlVal || "").trim().length === 0;
let isValid = !isWhitespace;
return isValid ? null : { whitespace: "value is only whitespace" };
};
}
Mã này khá đơn giản:
- Lấy giá trị của control.
- Kiểm tra nếu giá trị là số (vì bạn có thể sử dụng nó với
<input type="number">
), hãy chuyển đổi giá trị đó thành chuỗi. - Kiểm tra độ dài của chuỗi sau khi đã trim, nếu độ dài vẫn là 0, chắc chắn rằng input chỉ chứa dấu cách.
- Dựa vào đó và trả lại lỗi hoặc null.
Bây giờ, hãy thay thế Validators.required bằng NoWhitespaceValidator và kiểm tra. Với custom validator này, bạn có thể kiểm soát dễ dàng hơn việc xác định trường input không chứa các ký tự khoảng trắng.