Giới thiệu và Thông báo
Trước tiên, các API được đề cập trong bài viết này vẫn đang trong giai đoạn thử nghiệm và có thể sẽ thay đổi trong tương lai. Như với tất cả các API thử nghiệm, tôi không khuyến khích sử dụng chúng trong các ứng dụng dự kiến đưa vào sản xuất trong thời gian gần.
Hiện tại, không có gì ngạc nhiên khi Đội ngũ Angular đang cố gắng tích hợp các tín hiệu càng nhiều càng tốt vào các API hiện có. Tất nhiên, biểu mẫu cũng không bị bỏ lại, và một loại biểu mẫu mới đang xuất hiện: Biểu Mẫu Signal. Sự bổ sung này làm tăng tổng số các loại biểu mẫu trong Angular lên ba:
- Biểu Mẫu React: biểu mẫu được kiểm soát bởi thành phần
- Biểu Mẫu Dựa trên Mẫu: biểu mẫu được kiểm soát bởi mẫu
- Biểu Mẫu Signal: biểu mẫu được kiểm soát bởi tín hiệu
Tư duy về Biểu Mẫu Signal
Dù bạn sử dụng framework hay thư viện nào để tạo biểu mẫu, một biểu mẫu đơn giản chỉ là một tập hợp các trường UI cho phép người dùng nhập dữ liệu có cấu trúc, có thể được quản lý bởi các quy tắc xác thực để đảm bảo tính toàn vẹn của nó.
Với Biểu Mẫu Signal, khái niệm này được chia thành bốn phần riêng biệt:
- Mô hình dữ liệu: mô hình dữ liệu hiện tại của biểu mẫu
- Trạng thái Trường: siêu dữ liệu liên quan đến một trường biểu mẫu (giá trị, tính hợp lệ và trạng thái điều khiển)
- Logic Trường: logic nghiệp vụ của trường (quy tắc xác thực, hiển thị có điều kiện của trường)
- Điều khiển UI: điều khiển cho phép tương tác giữa các phần tử gốc, các thành phần tùy chỉnh và người dùng.
Một trong những điểm chính của biểu mẫu Signal là nó không duy trì dữ liệu của riêng mình bên trong. Thay vào đó, chúng ta, các nhà phát triển, sẽ cung cấp mô hình dữ liệu này thông qua một tín hiệu mà thư viện sẽ sử dụng làm nguồn duy nhất cho các trường của biểu mẫu của chúng ta.
Để minh họa điều này qua mã:
typescript
interface Assigned {
name: string;
firstname: string;
}
interface Todo {
title: string;
description: string;
status: TodoStatus;
assigned: Assigned[];
}
@Component({
selector: 'app-form',
templateUrl: './app-form.html'
})
export class AppForm {
todoModel = signal<Todo>({
title: '',
description: '',
status: 'not_begin',
assigned: []
}); // Tạo mô hình sẽ là nguồn sự thật cho biểu mẫu và cây trường của nó
todoForm = form(this.todoModel); // Tạo biểu mẫu liên kết với mô hình
}
Việc thiết lập mô hình là nguồn sự thật duy nhất có nghĩa là hai điều quan trọng:
- Bất kỳ thay đổi nào đối với mô hình, thông qua hàm set hoặc update, sẽ tự động cập nhật trường biểu mẫu.
- Bất kỳ thay đổi nào đối với biểu mẫu do tương tác của người dùng với một trường sẽ cập nhật mô hình.
Cây Trường
Gọi hàm form cung cấp cho nhà phát triển quyền truy cập vào một cây Trường. Biểu mẫu bản thân là một Trường được gọi là Trường Gốc.
Một thực thể Field cung cấp trạng thái của nó, cho phép bạn truy xuất giá trị, tính hợp lệ và nhiều hơn nữa và nó có thể được truy xuất bằng cách gọi hàm Field.
Hãy minh họa điều đó với một chút mã:
typescript
interface Assigned {
name: string;
firstname: string;
}
interface Todo {
title: string;
description: string;
status: TodoStatus;
assigned: Assigned[];
}
@Component({
selector: 'app-form',
templateUrl: './app-form.html'
})
export class AppForm {
todoModel = signal<Todo>({
title: '',
description: '',
status: 'not_begin',
assigned: []
}); // Tạo mô hình sẽ là nguồn sự thật cho biểu mẫu và cây trường của nó
todoForm = form(this.todoModel); // Tạo biểu mẫu liên kết với mô hình
titleField: Field<string> = this.todoForm.title;
firstAssigned: Field<Assigned> = this.todoForm.assigned[0];
firstAssignedName: Field<string> = firstAssigned.name;
}
Thực thể Trường
Như đã giải thích trước đó, một thực thể Trường trả về trạng thái của trường đó. Trạng thái bao gồm 6 điểm chính:
- value: Một WritableSignal cho phép bạn đọc và ghi giá trị của một trường.
- errors: Một signal để truy xuất các lỗi xác thực trên trường.
- valid: Một signal để truy xuất tính hợp lệ của trường.
- disabled: Một signal để truy xuất xem trường có bị vô hiệu hóa hay không.
- touched: Một signal để biết liệu người dùng đã tương tác với trường hoặc một trong các con của nó hay chưa.
- dirty: Một signal để biết liệu trường hoặc một trong các con của nó có thay đổi hay không.
Để biết thêm chi tiết về những gì một thực thể Trường có thể cung cấp, hoặc để xem cách triển khai cụ thể, hãy tham khảo mã bên dưới:
typescript
interface Assigned {
name: string;
firstname: string;
}
interface Todo {
title: string;
description: string;
status: TodoStatus;
assigned: Assigned[];
}
@Component({
selector: 'app-form',
template: `<button [disabled]="titleField().valid()">Gửi</button>`
})
export class AppForm {
todoModel = signal<Todo>({
title: '',
description: '',
status: 'not_begin',
assigned: []
}); // Tạo mô hình sẽ là nguồn sự thật cho biểu mẫu và cây trường của nó
todoForm = form(this.todoModel); // Tạo biểu mẫu liên kết với mô hình
titleField: Field<string> = this.todoForm.title;
firstAssigned: Field<Assigned> = this.todoForm.assigned[0];
firstAssignedName: Field<string> = firstAssigned.name;
}
Liên kết trường với các phần tử UI
Chúng ta đã hoàn thành việc định nghĩa mô hình dữ liệu, tạo biểu mẫu, điều hướng cây trường và truy xuất thực thể trường.
Bây giờ, điều quan trọng là kết nối phần tử UI (input, textarea hoặc thậm chí là một thành phần tùy chỉnh) với trường để người dùng có thể tương tác với điều khiển.
Trong Angular, Biểu Mẫu Signal cung cấp một chỉ thị tích hợp, vì vậy bạn có thể xem mã nguồn tại đây. Chỉ thị này sẽ có một số trách nhiệm:
- Liên kết dữ liệu hai chiều để cập nhật giá trị của trường (cả từ tương tác của người dùng và lập trình).
- Liên kết logic nghiệp vụ của trường (quy tắc xác thực, trạng thái chỉ đọc, v.v.).
- Chuyển tiếp các sự kiện khác nhau có thể xảy ra trên điều khiển (bẩn, đã tương tác, v.v.).
- Nội bộ, tiêm token NgControl để có được các phương thức hữu ích và đảm bảo khả năng tương tác.
Tại đây, chúng ta rõ ràng thấy ý định tạo cầu nối giữa logic HTML và biểu mẫu của Angular, tận dụng tất cả sức mạnh mà chúng có thể cung cấp.
Hãy vật chất hóa điều này với một số mã:
typescript
interface Assigned {
name: string;
firstname: string;
}
interface Todo {
title: string;
description: string;
status: TodoStatus;
assigned: Assigned[];
}
@Component({
selector: 'app-form',
templateUrl: './app-form.html',
imports: [Control]
})
export class AppForm {
todoModel = signal<Todo>({
title: '',
description: '',
status: 'not_begin',
assigned: []
}); // Tạo mô hình sẽ là nguồn sự thật cho biểu mẫu và cây trường của nó
todoForm = form(this.todoModel); // Tạo biểu mẫu liên kết với mô hình
}
html
<form novalidate>
<input type="text" [control]="todoForm.title" />
<input type="text" [control]="todoForm.description" />
<select [control]="todoForm.status">
<option [ngValue]="not_begin">Chưa bắt đầu</option>
<option [ngValue]="in_progress">Đang tiến hành</option>
<option [ngValue]="finished">Đã hoàn thành</option>
</select>
</form>
Kết luận
Bài viết này đánh dấu sự kết thúc của bài đầu tiên trong loạt ba bài. Bài viết này nhằm mục đích đặt nền tảng cho các biểu mẫu của Signal.
Chúng ta đã có thể hiểu cách hoạt động của các biểu mẫu Signal bằng cách chi tiết các thành phần chính của chúng.
Tuy nhiên, cho đến bây giờ, biểu mẫu của chúng ta không chứa xác thực, và nhắc nhở rằng xác thực bao gồm logic nghiệp vụ của trường của chúng ta. Bước này sẽ được trình bày chi tiết trong bài viết thứ hai.
Điều cần nhớ ở đây là các nhà phát triển có toàn quyền kiểm soát mô hình dữ liệu mà họ cung cấp. Mô hình dữ liệu này sẽ cho phép kết nối giữa Field và giá trị của nó.
Hàm form cho phép tạo ra một cây trường. Có thể điều hướng cây này bằng cách sử dụng cú pháp dấu chấm.
Một thực thể Field cho phép bạn truy xuất trạng thái của nó bằng cách gọi một hàm, cho phép bạn, ví dụ, biết giá trị hiện tại của nó.