Yup là gì?
Yup là một thư viện để validate form. Yup validate dữ liệu dựa trên schema được định nghĩa của form, là thư viện giúp lập trình viên web frontend dễ dàng hơn với validate dữ liệu, tập trung vào việc phát triển ứng dụng và tiết kiệm được thời gian làm việc cũng như hạn chế lỗi lập trình xảy ra.
Nó có hơn bốn triệu lượt tải hàng tuần với kích thước 243kb tại thời điểm viết blog.
Nó có hơn hai mươi nghìn sao trên github tại thời điểm viết blog.
Các dữ liệu trên đã chứng minh được tính mạnh mẽ, cộng đồng sử dụng lớn và mức độ tin tưởng để sử dụng thư viện này vào các dự án của mình.
Cách sử dụng Yup
Chia thành 5 cấp độ làm việc với Yup từ sử dụng cơ bản đến nâng cao!
Cấp độ 1 – Sử dụng cơ bản
Việc sử dụng Yup gồm 2 bước:
+ Bước: 1: Định nghĩa schema
Bạn định nghĩa schema bằng yup.object.shape(). Tham số truyền vào là một object chứa các field và các định nghĩa validate của field đó.
+ Bước 2: Truyền data vào để validate nó.
Trong ví dụ dưới đây, mình định nghĩa name là kiểu chuỗi, bắt buộc, age kiểu số và giá trị nhỏ nhất phải là 18.
1. import * as yup from "yup";
2.
(async () => {
3. let schema = yup.object().shape({
4. name: yup.string().required(),
5. age: yup.number().min(18),
6. });
7.
try {
8. await schema.validate({ name: "test", age: 11 });
9. } catch (error) {
10. console.log(error.errors);
11. }
12. })();
Khi mình truyền data với name là “test”, age là 11 thì mình sẽ nhận về một lỗi là một mảng dữ liệu với message: “age must be greater than or equal to 18”
Khi có lỗi, Yup sẽ trả về cho bạn một đối tượng lỗi ValidationError, bên dưới là định nghĩa ValidationError trên document của Yup.
Đối tượng ValidationError gồm:
- name với giá trị là “ValidationError”
- type là loại lỗi
- value là giá trị được test
- params là các tham số như max value, regex
- path là field lỗi
- errors là một array message
- inner: Khi abortEarly là false sẽ trả về một array với mỗi item là một ValidationError
Ở trên mình có nhắc đến abortEarly, vậy abortEarly là gì?
abortEarly sẽ có hai giá trị:
+ true: khi validate đến một field bị lỗi sẽ trả về lỗi luôn mà không validate tiếp những field khác.
+ false: sẽ validate toàn bộ schema và lấy tất cả các lỗi ra.
Hãy xem ví dụ sau:
1. import * as yup from "yup";
2.
(async () => {
3. let schema = yup.object().shape({
4. name: yup.string().required(),
5. age: yup.number().min(18)
6. });
7.
try {
8. await schema.validate({ name: null, age: 11 }, { abortEarly: false });
9. } catch (error) {
10. console.log(error.errors);
11. }
12. })();
Khi chạy đoạn code trên, kết quả sẽ trả về một array gồm hai item ở dạng object ValidationError.
1. import * as yup from "yup";
2.
(async () => {
3. let schema = yup.object().shape({
4. name: yup.string().required(),
5. age: yup.number().min(18)
6. });
7.
try {
8. await schema.validate({ name: null, age: 11 }, { abortEarly: true });
9. } catch (error) {
10. console.log(error.inner);
11. }
12. })();
Khi chạy đoạn code trên, kết quả sẽ trả về một array gồm một item là field validate lỗi đầu tiên.
Như bạn thấy, message trả về là message mặc định của Yup, vậy nếu muốn tùy chỉnh nội dung message thì làm thế nào?
Bạn chỉ cần truyền message tùy chỉnh vào function mà bạn validate. Như ví dụ bên dưới, mình đã tùy chỉnh hai message trả về của field name ở method required và field age ở method min.
import * as yup from "yup";
(async () => {
let schema = yup.object().shape({
name: yup.string().required('Please input name'),
age: yup.number().min(18, 'Please input age')
});
try {
await schema.validate({ name: null, age: 11 }, { abortEarly: true });
} catch (error) {
console.log(error.inner);
}
})();
Yup cung cấp các kiểu dữ liệu sau:
- String
- Number
- Boolean
- Date
- Array
- Object
- Mixed/Schema
Từng kiểu dữ liệu, Yup cũng đã cung cấp các method cho nó như kiểu String gồm: required, length, min, max …
Bạn có thể tham khảo thêm tại https://github.com/jquense/yup/tree/pre-v1#api
Cấp độ 2 – tùy chỉnh logic xử lý
Nếu bạn cần một logic mà không có method nào được Yup cung cấp sẵn, bạn sẽ cần tùy chỉnh logic xử lý. Hãy sử dụng method test, bên dưới là định nghĩa test trên document của Yup.
Method test gồm 3 tham số:
+ name: tên của tùy chỉnh logic
+ message: nội dung message sẽ được trả về nếu dữ liệu không khớp với logic xử lý.
+ function: hàm viết nội dung của logic xử lý. Hàm sẽ trả về true nếu hợp lệ và false nếu không hợp lệ.
Xét ví dụ bên dưới:
import * as yup from "yup";
(async () => {
let schema = yup.object().shape({
name: yup
.string()
.required("Please input name")
.test("name-not-includes-spam", "Name must not spam", (value) => {
return !value.includes("spam");
}),
age: yup.number().min(18, "Please input age"),
});
try {
await schema.validate({ name: "spam", age: 11 }, { abortEarly: false });
} catch (error) {
console.log(error.inner);
}
})();
Mình định nghĩa method tùy chỉnh logic với hàm test tên là “name-not-includes-spam”, message là “Name must not spam”. Dữ liệu truyền vào gồm chữ spam, khi chạy code trên sẽ nhận được message “Name must not spam”.
Method test bạn có thể sử dụng nhiều lần để validate nhiều logic được nhé!
Cấp độ 3 – tham chiếu
Ví dụ mình có một form set password, form gồm field password và confirm-password. Mình muốn validate là field confirm-pasword có giống với password hay không?
Mình sẽ sử dụng hai method là ref và oneOf mà Yup cung cấp, bên dưới là định nghĩa ref và oneOf trên document của Yup.
+ ref sẽ tham chiếu đến field trong schema
+ oneOf là field đó phải chứa giá trị nằm trong mảng được cung cấp trong param truyền vào oneOf.
Xét đoạn code bên dưới:
import * as yup from "yup";
(async () => {
let schema = yup.object().shape({
password: yup.string().required(),
passwordConfirm: yup.string().required().oneOf([yup.ref('password')], 'Password confirm not matching'),
});
try {
await schema.validate({ password: "123", passwordConfirm: '1234' }, { abortEarly: false });
} catch (error) {
console.log(error.inner);
}
})();
Khi chạy đoạn code trên sẽ nhận về message lỗi “Password confirm not matching”
Tôi có thể sử dụng ref trong method test không?
Câu trả lời là không. Thay vào đó bạn có thể sử dụng this.parent.password như sau:
import * as yup from "yup";
(async () => {
let schema = yup.object().shape({
password: yup.string().required(),
passwordConfirm: yup
.string()
.required()
.test("password-matching", "Passwords do not match", function (value) {
return value === this.parent.password;
}),
});
Cấp độ 4 – Tùy chỉnh điều kiện validate
Ví dụ mình có một switch và một input. Mình muốn khi switch được bật lên thì sẽ kiểm tra input nhập vào có phải là url hay không? Nếu như switch không được bật, sẽ không validate input này.
Yup có cung cấp một method when, method gồm các tham số:
+ key: tên field cần xét điều kiện
+ object builder: là điều kiện xử lý gồm: is, then và otherwise. is chứa điều kiện field đó bằng với giá trị gì. Nếu is là true thì sẽ thực hiện then, ngược lại sẽ thực hiện otherwise. Thuộc tính otherwise là tùy chọn, có thể có hoặc không. Thuộc tính is có thể là một function.
Bên dưới là định nghĩa when trên document của Yup.
Xét ví dụ bên dưới:
import * as yup from "yup";
(async () => {
let schema = yup.object().shape({
active: yup.boolean().required(),
url: yup.string().when("active", {
is: true,
then: (schema) => schema.required().url(),
})
});
try {
await schema.validate({ active: true, url: "123" }, { abortEarly: false });
console.log("data is valid");
} catch (error) {
console.log(error.inner);
}
})();
Mình đã gắn method when vào field url, nếu field active có giá trị true thì thì sẽ kiểm tra thêm required và url cho field url.
Khi chạy đoạn code trên sẽ trả về message “url must be a valid URL”
Cấp độ 5 – lỗi kiểu dữ liệu
Xét ví dụ bên dưới:
1. import * as yup from "yup";
2.
(async () => {
3. let schema = yup.object().shape({
4. active: yup.boolean().required(),
5. url: yup.string().when("active", {
6. is: true,
7. then: (schema) => schema.required().url()
8. }),
9. view: yup.number
10. });
11.
try {
12. await schema.validate(
13. { active: false, url: "123", view: "" },
14. { abortEarly: false }
15. );
16. console.log("data is valid");
17. } catch (error) {
18. console.log(error.inner);
19. }
20. })();
Mình đã định nghĩa schema field view là kiểu number, nhưng dữ liệu mình truyền vào lại là kiểu string. Thì khi chạy đoạn code trên nó sẽ trả về lỗi mặc định như bên dưới.
Như bạn thấy, lỗi mặc định không thân thiện với người dùng. Mình muốn tùy chỉnh message như thế nào nhỉ?
Trước tiên, mình sẽ cung cấp cho các bạn cách mà Yup làm để xử lý lỗi kiểu dữ liệu này.
+ Với kiểu string, Yup sẽ sử dụng phương thức toString để ép kiểu dữ liệu cho dữ liệu mà người dùng truyền vào.
+ Với kiểu number, Yup sẽ sử dụng phương thức parseFloat để ép kiểu dữ liệu cho dữ liệu mà người dùng truyền vào. Nếu lỗi sẽ trả về NaN.
+ Với kiểu date, Yup sẽ sử dụng hàm dựng Date để ép kiểu dữ liệu cho dữ liệu mà người dùng truyền vào. Nếu lỗi sẽ trả về Date không hợp lệ.
Đến đây, bạn đã hiểu được vì sao gây ra lỗi kiểu dữ liệu. Yup cung cấp method typeError với tham số message là nội dung sẽ trả về khi lỗi về kiểu dữ liệu.
Xét ví dụ bên dưới:
import * as yup from "yup";
(async () => {
let schema = yup.object().shape({
active: yup.boolean().required(),
url: yup.string().when("active", {
is: true,
then: (schema) => schema.required().url()
}),
view: yup.number().typeError('View must number')
});
try {
await schema.validate(
{ active: false, url: "123", view: "" },
{ abortEarly: false }
);
console.log("data is valid");
} catch (error) {
console.log(error.inner);
}
})();
Khi chạy đoạn code trên sẽ nhận về message “View must number”.
Kết luận
Trên đây là những chia sẻ của mình về sử dụng thư viện Yup cho việc validate dữ liệu phía frontend. Mong là bài viết này sẽ hữu ích cho các bạn khi tìm hiểu và ứng dụng thư viện Yup vào dự án.