[Angular JS] Validating input in reactive forms
Bài đăng này đã không được cập nhật trong 3 năm
Hiện nay gần như các hệ thống web đều sử dụng các form input và có các yêu cầu về tính đúng đắn của dữ liệu. Vì vậy việc thực hiện validate các trường dữ liệu rất thường gặp. Hôm nay mình sẽ trình bày về cách Angular thực hiện validate các trường trong 1 form input.
Validator functions
Validator functions có thể là đồng bộ (Sync validators) hoặc không đồng bộ (Async validators).
Sync validators: Các function đồng bộ sẽ dùng một control instance và trả về ngay lập tức một tập hợp các lỗi validation hoặc null. Bạn có thể chuyển chúng vào làm đối số thứ hai khi bạn khởi tạo một FormControl.
Async validators: Các function không đồng bộ lấy một control instance và trả về một Promise hoặc Observable sau đó tạo ra một tập hợp các lỗi validation hoặc null. Bạn có thể chuyển chúng vào làm đối số thứ ba khi bạn khởi tạo một FormControl.
Vì lý do hiệu suất, Angular chỉ chạy Async validators nếu đã pass tất cả các Sync validators. Mỗi loại phải hoàn thành trước khi lỗi được thiết lập.
Built-in validator functions
Bạn có thể tự custom các validator functions, hoặc bạn có thể sử dụng các buit-in validators của Angular.
Các built-in validators có sẵn dưới dạng thuộc tính như trong template-driven forms, ví dụ như required và minlength, tất cả đều có sẵn để sử dụng như các functions từ Validators class. Để có list đầy đủ các built-in validators, hãy xem Validators API reference.
Ví dụ ta sẽ tạo một hero form như sau:
reactive/hero-form-reactive.component.ts (validator functions)
ngOnInit(): void {
this.heroForm = new FormGroup({
name: new FormControl(this.hero.name, [
Validators.required,
Validators.minLength(4),
forbiddenNameValidator(/bob/i) // <-- Here's how you pass in the custom validator.
]),
alterEgo: new FormControl(this.hero.alterEgo),
power: new FormControl(this.hero.power, Validators.required)
});
}
get name() { return this.heroForm.get('name'); }
get power() { return this.heroForm.get('power'); }
Trong ví dụ trên, name control thiết lập 2 built-in validators là Validators.required và Validators.minLength (4) —và một custom validator là forbiddenNameValidator.
Tất cả các validators này đều đồng bộ, vì vậy chúng được dùng làm đối số thứ hai. Lưu ý rằng bạn có thể hỗ trợ nhiều validators bằng cách chuyển các functions vào dưới dạng một mảng.
Ví dụ này cũng thêm một vài phương thức getter. Trong một reactive form, bạn luôn có thể truy cập vào bất kỳ form control nào thông qua phương thức get trên parent group.
Template cho form input:
reactive/hero-form-reactive.component.html (name with error msg)
<input type="text" id="name" class="form-control"
formControlName="name" required>
<div *ngIf="name.invalid && (name.dirty || name.touched)"
class="alert alert-danger">
<div *ngIf="name.errors?.required">
Name is required.
</div>
<div *ngIf="name.errors?.minlength">
Name must be at least 4 characters long.
</div>
<div *ngIf="name.errors?.forbiddenName">
Name cannot be Bob.
</div>
</div>
Defining custom validators
Built-in validators không phải lúc nào cũng phù hợp với trường hợp sử dụng mà bạn mong muốn. Vì vậy, đôi khi bạn cần tạo custom validatator.
Hãy xem xét function forbiddenNameValidator từ các ví dụ reactive-form trước đó. Ta định nghĩa function đó như sau:
shared/forbidden-name.directive.ts (forbiddenNameValidator)
/** A hero's name can't match the given regular expression */
export function forbiddenNameValidator(nameRe: RegExp): ValidatorFn {
return (control: AbstractControl): ValidationErrors | null => {
const forbidden = nameRe.test(control.value);
return forbidden ? {forbiddenName: {value: control.value}} : null;
};
}
Function trên là một factory lấy một regular expression để phát hiện name vi phạm yêu cầu cụ thể và trả về một validatior function.
Ví dụ, khi bạn chỉ cho nhập tên tiếng nhật (chữ Hira và Kana) thì khi nhập chữ Latin sẽ báo lỗi, khi đó thì built-in validators không hỗ trợ buộc bạn custom 1 function validator để thực hiện việc đó.
forbiddenNameValidator factory trả về validator function đã định cấu hình. Function đó nhận một Angular control object và trả về null nếu control value là hợp lệ hoặc một error object. Validation error object thường có một thuộc tính có key là 'forbiddenName' và có giá trị là một từ điển giá trị tùy ý mà bạn có thể chèn vào thông báo lỗi.
Custom async validators tương tự như sync validators, nhưng thay vào đó, chúng phải trả về Promise hoặc observable mà sau đó tạo ra null hoặc validation error object.
Adding custom validators to reactive forms
Trong reactive forms, ta thêm custom validator bằng cách chuyển trực tiếp function tới FormControl.
reactive/hero-form-reactive.component.ts (validator functions)
this.heroForm = new FormGroup({
name: new FormControl(this.hero.name, [
Validators.required,
Validators.minLength(4),
forbiddenNameValidator(/bob/i) // <-- Here's how you pass in the custom validator.
]),
alterEgo: new FormControl(this.hero.alterEgo),
power: new FormControl(this.hero.power, Validators.required)
});
Control status CSS classes
Angular tự động thêm 1 số class khi xuất hiện lỗi. Bạn có thể sử dụng các class này để css cho các element theo trạng thái của form. Các class hiện đang được hỗ trợ:
- .ng-valid
- .ng-invalid
- .ng-pending
- .ng-pristine
- .ng-dirty
- .ng-untouched
- .ng-touched
Ví dụ dưới đây, hero form sử dụng class .ng-valid và .ng-invalid để set màu cho mỗi border field:
forms.css (status classes)
.ng-valid[required], .ng-valid.required {
border-left: 5px solid #42A948; /* green */
}
.ng-invalid:not(form) {
border-left: 5px solid #a94442; /* red */
}
.alert div {
background-color: #fed3d3;
color: #820000;
padding: 1rem;
margin-bottom: 1rem;
}
.form-group {
margin-bottom: 1rem;
}
label {
display: block;
margin-bottom: .5rem;
}
select {
width: 100%;
padding: .5rem;
}
Tổng kết
Trên đây mình đã trình bày tổng quan về cách validation trong Angular, cách custom các function của riêng mình, một số class hỗ trợ style cho element khi có valid/invalid. Hy vọng bài viết sẽ có ích đối với mọi người.
Tài liệu tham khảo:
All rights reserved