Với những ai làm nhiều về Form sẽ biết: Làm Form không chỉ đơn giản là trò chơi lego - tìm cách sắp xếp, lồng ghép các viên gạch (Ở đây là những combo_box, text_box, table,...) sao cho hợp lý và dễ thao tác; Mà còn là trò chơi đầu bếp nơi bạn phải xào nấu, chế biến API (Và rõ ràng giống như nấu ăn bạn phải hiểu rõ gia vị, thực phẩm của bạn - chính là dữ liệu, cấu trúc và mối quan hệ giữa chúng).
Tin mình đi hầu hết các vấn đề phức tạp về việc load dữ liệu lên hay đưa dữ liệu xuống hoàn toàn có thể xử lý nhanh gọn chỉ bằng việc sửa đổi lại "lưới" API thay vì một mớ hầm bà lằng code trên giao diện.
Okay mấy chế khoan hãy xoắn 🤣 nói như vậy để mấy chế thấy rằng mọi thứ hãy suy nghĩ nó như trò chơi! Cứ từ từ thì cháo nó cũng nhừ, trước tiên thì cứ xử lý “thằng” front-end cái đã (Dù sao thì đảm bảo qua loạt bài viết này bạn cũng đã có kha khá kiến thức toàn tập tập rồi, thêm vài cái chuyên đề back-end nữa là ngon ghẻ liền ^^).
I. ĐÔI LỜI LUẬN BÀN
Việc kiểm soát nhập liệu từ phía người dùng bằng Form luôn là nền tảng cơ sở của rất nhiều ứng dụng – (Ứng dụng mà không có dữ liệu, đặc biệt là dữ liệu từ phía người dùng, thì là ứng dụng “cô đơn – FA” nên ít nhất thì đừng để ứng dụng của bạn chịu chung tình cảnh với bạn chứ đúng không… 😖 )
Form có mặt trong mọi hoạt động (từ đăng nhập, cập nhật profile, thêm những dữ liệu “nhạy cảm” cho đến thực hiện rất nhiều các tác vụ dữ liệu đầu vào…). Tin mình đi ngay cả trong game mà còn có váng dóc của ‘thằng ku’ này nữa mà nói chi là các ứng dụng web.
Và Angular hiểu tầm quan trọng của Form nên đã linh hoạt cung cấp cho chúng ta tận hai giải pháp tiếp cận làm Form: Dạng động (Reactive) và Dạng tĩnh (Template-driven) [Mình dịch thoáng cho dễ hiểu/nghe thôi nhen, mà thường thực tế người ta cũng gọi vậy hoặc gọi luôn bằng tên tiếng Anh :v]
Trong Phần 1 này chúng ta khoan hãy đi vào chi tiết cách sử dụng từng loại Form mà hay đi lý thuyết trước về định nghĩa, phân biệt và rất nhiều khái niệm bạn cần nằm trước.
II. Động hay Tĩnh đây?

Cả hai giải pháp đều có khả năng bắt được các sự kiện input từ view, kiểm tra chúng, tạo form model và data model để update cũng như cung cấp cách để theo dõi thay đổi. Vậy diều khác nhau là gì?

@Template-Driven: Viết Form bằng HTML code sau đó Angular sẽ tự động suy ra cấu trúc của Form (Form Controls nào mà nó có hay chứa những trường inputs nào ?) – Nói ngắn gọn là mọi thứ mấy bồ làm với Form, mấy bồ sẽ làm trên HTML template (Nói sâu hơn là làm trên Form object tạo bởi Angular).
@Reactive: Định nghĩa cấu trúc của Form bằng code Typescript (Tất nhiên vẫn phải có cái khung HTML nhưng ở đây chúng ta connect với nó thủ công chứ không sung sướng như trên được – Form object vì vậy cũng là do ta tự tạo luôn, nói chung là hàng chính chủ á :v).
Okay trước khi vào cụ thể từng giải pháp hãy điểm qua những cụm từ thông dụng bạn sẽ được nhắc hoài:
Lưu ý: FormControl, FormGroup, FormArray, ControlValueAccessor t huộc về nhóm Building Block cơ bản của Angular Form.
- FormControl: Theo định nghĩa của Angular Team thì nó là thằng sẽ theo dõi giá trị và validation của những form control. [Cơ mà cứ nghĩ đơn giản là nó là cấp đơn vị nhỏ nhất trong Form, các thành phần của Form như field, combo-box,…vân vân và mây mây các controls khác,… chỗ nào mà chứa dữ liệu thì chỗ đó là FormControl :v ].
- FormGroup: Cái tên nói lên tất cả, nó là thằng chứa các FormControls; Tất nhiên nếu bạn thao tác trên nó thì cũng là thao tác với tất cả các FormControls trong nó.
- FormArray: Okay hãy hiểu nó như là một biến thể của FormGroup và dữ liệu của nó được sắp xếp như là một mảng thay vì như là một tập hợp các đối tượng (controls) đối với FormGroup. Và điều này càng hữu dụng dối với Form động vì như một mảng thì nó sẽ được đánh số, bạn có thể chủ động thao tác các phần tử trong mảng như thêm, xóa tại vị trí (index) nào đó.
- ControlValueAccessor:
- Form Model – FormControl Instances – Control Instances): Hmmm chỗ này mình tin là nhiều bạn sẽ thắc mắc nên chúng ta hãy đi sâu vào tìm hiểu một chút nhen…
III. Form Model Setup
Q: Thế FormModel, FormControl Instances, Control Instances rồi thậm chí FormModel Instance có dây mơ rễ má gì với nhau? :v
A: Giải thích chay sẽ khó hiểu nên nhìn code và giải thích bên dưới cho dễ mường tượng nhen:
*Reactive Form
...
export class AppComponent implements OnInit {
signupForm: FormGroup;
constructor() {}
ngOnInit() {
this.signupForm = new FormGroup({
'userData': new FormGroup({
'username': new FormControl(null, [Validators.required]),
'email': new FormControl(null, [Validators.email])
}),
'gender': new FormControl('male'),
'hobbies': new FormArray([])
});
}- Ở đây signupForm chính là Form Model.
- Mỗi một lần new FormControl sẽ tạo ra một FormControl instance (gọi ngắn gọn là Control Instance).
*Template-Driven Form
Với Template-Driven Form thì Form Model có thể nói là toàn bộ HTML Form template (toàn bộ trong cái thẻ <form> á).
Có thể bạn chưa biết: Có thể bạn sẽ gặp cụm từ “the source of truth” trong một số tài liệu. Cụm từ này mô tả việc trường Input lấy giá trị ở đâu và thay đổi trên Input sẽ thay đổi giá trị nào.
Theo như ví dụ về Reactive Form ở trên thì “The source of truth” cũng chính là “signupForm” nên nếu nói nó là Form Model cũng không hẳn là sai.
Cả hai phương pháp tĩnh và động đều sử dụng Form Model để theo dõi những sự thay đổi giá trị giữa các form Angular và các phần tử form input.
Dưới đây sẽ là ví dụ chỉ ra cách mà Form Model được định nghĩa và tạo ra.
a. Setup in Reactive Form
import { Component } from '@angular/core';
import { FormControl } from '@angular/forms';
@Component({
selector: 'app-reactive-favorite-color',
template: `
Favorite Color: <input type="text" [formControl]="favoriteColorControl">
`
})
export class FavoriteColorComponent {
favoriteColorControl = new FormControl('');
}Đoạn code trên cho ta thấy:
– Tại thời điểm khởi tạo FormControl instance nó cũng đồng thời khởi tạo cho ta giá trị và trạng thái của Form Element (thẻ <input> trong template).
– Form Model như ví dụ ở đây cũng chính là FormControl instance. (‘favoriteColorControl’).

Đối với Reactive Form, Form Model được định nghĩa rõ ràng ngay tại component class (file .ts). Reactive Form Directive (FormControlDirective – trong trường hợp ở trên là [formControl] trong template) sẽ liên kết FormControl Instance với Form Element của riêng nó trong view bằng cách sử dụng một Value Accessor (ControlValueAccessor instance – Ở đây chính là ‘favoriteColorControl’ được gán vào [formControl])
b. Setup in template-driven form
Dưới dây là đoạn code giống trên nhưng dùng phương pháp tạo form tĩnh
import { Component } from '@angular/core';
@Component({
selector: 'app-template-favorite-color',
template: `
Favorite Color: <input type="text" [(ngModel)]="favoriteColor">
`
})
export class FavoriteColorComponent {
favoriteColor = '';
}Trái với Reactive Form thì source of truth ở đây chính là nguyên cái template.

ngModel directive sẽ chịu trách nhiệm tạo và quản lý các FormControl instance cho các Form element chứa nó. Mặc dù nó ít rõ nét hơn giải pháp ở trên (ở trên việc tạo FormControl được gom lại một chỗ, hơn nữa còn có từ khóa new để nhận biết; Còn với cách dùng directive thì chỉ có mỗi việc khai báo ngModel như trên).
IV. Data Flow
Khi xây dựng form trong Angular, điều quan trọng là ta phải hiểu được cách mà framework quản lý luồng dữ liệu từ người dụng hoặc từ những thay đổi lập trình. Reactive và Template-driven form sử dụng hai cách khác nhau khi quản lý form input. Bây giờ ta lấy ví dụ favorite color ở trên để demo luồng dữ liệu sẽ đi như thế nào từ một trường giá trị input bị thay đổi ở view tới model.
a. Data Flow in Reactive Form
Như phần trên đã giới thiệu thì trong Reactive Form, mỗi Element ở View được liên kết trực tiếp với Form Model (FormControl instance). Việc Update từ view tới model và ngược lại như vậy trở thành một luồng đồng bộ (synchronous – không có vụ nhảy cóc í, công việc tiến hành theo trình tự) và không phụ thuộc vào việc render UI.

Các bước mà luồng dữ liệu sẽ đi từ view tới model:
1. Người dùng nhập giá trị vào input element (ở đây là Blue)
2. Form Input element sẽ emit (phát đi) một sự kiện input (“input” event) với giá trị mới nhất.
3. Control Value Accessor sẽ lắng nghe các sự kiện ở trên input element và ngay lập tức truyền tiếp giá trị mới vừa nhận sang cho FromControl instance.
4. FormControl instance sẽ phát (emit) giá trị mới đó qua valueChanges observable.
5. Bất kì subcribers tới valueChanges observable nhận được giá trị mới nhất của control đó.

Data Flow trong Reactive Model (Model to View)
Các bước mà luồng dữ liệu sẽ đi từ model tới view:
1. User gọi phương thức favoriteColorControl.setValue( ) – setValue dùng để update giá trị của FormControl.
2. FormControl instance phát (emit) giá trị mới qua valueChanges observable.
3. Bất cứ subcriber nào subcribe valueChanges observable sẽ nhận được giá trị mới nhất của control đó.
4. Control Value Accessor trên Input Element sẽ update element đó với giá trị vừa nhận.
b. Data Flow in Template-Driven Form
Điểm khác biết so với ở trên là đối với Template-Driven Form, mỗi Element được liên kết với một directive (thay vì Form Model thông qua Value Accessor) và directive chịu trách nhiệm việc quản lý Form model ở nội tại bên trong nó.

Các bước mà luồng dữ liệu sẽ đi từ View tới Model khi input value thay đổi từ Red sang Blue:
1. User nhập giá trị mới: “Blue”
2. Input Element sẽ emit input event (chứa value = “Blue”)
3. Control_Value_Accessor có chứa input trigger (Trigger là cò súng, nghĩa là có gì tác động thì nó sẽ nổ – Và ở đây tác động chính là sự thay đổi giá trị) sẽ trigger phương thức setValue( ) trên FormControl instance.
4. FormControl instance sẽ emit giá trị mới qua valueChanges observable
5. Bất cứ subcriber đến valueChanges observable đểu nhận được giá trị mới nhất của control đó.
—
6. Control_Value_Accessor cũng gọi phương thức NgModel.viewToModelUpdate( ) để emit ngModelChange event.
7. Bởi vì component template sử dụng 2-way data binding đối với thuộc tính favoriteColor nên thuộc tính này trong component sẽ được update value được emit kia bởi ngModelChange event (Value ở đây là Blue).

Data Flow trong Template Driven Form (Model to View)
Dưới đây là các bước ngược lại ở trên từ Model tới View:
1. favoriteColor thay đổi giá trị ở file .ts
2. Cơ chế phát hiện thay đổi bắt đầu
3. Trong quá trình cơ chế trên thực hiện, ngOnChanges lifecycle hook (kiểu như thằng ngOnChanges sẽ được bật cho tới khi cá cơ chế trên kết thúc) được gọi ở NgModel directive instance bởi vì giá trị của có giá trị input thay đổi.
4. ngOnChanges( ) cho vào hàng đợi các tác vụ bất đồng bộ để set value cho nội tại bên trong của FormControl instance.
5. Change detection completes.
6. Tiếp theo, tác vụ set value cho FormControl instance được thực hiện.
7, FormControl instance emit giá trị mới nhất thông qua valueChanges observable.
8. Bất cứ subcriber đến valueChanges observable đểu nhận được giá trị mới nhất của control đó.
9. Control_Value_Accessor update Input Element trong view với giá trị mới nhất của favoriteColor.
Q: Okay ít nhiều gì cũng sẽ có mấy chế hỏi cái thằng valueChanges observable là cái vẹo gì? rồi thì ủa subcribe để lấy giá trị là sao?
A: Okay việc đầu tiên là “Hãy đọc qua bài viết về Observable của mình trước đã” :)))))) và có thể đọc thêm tại đây: https://bit.ly/2HMpsHs
– valueChanges là một thuộc tính của các lớp FormControl, FormArray, FormGroup. Nó trả về một Observable instance và do là một Observable chúng ta cần subcribe nó để lấy dữ liệu (Lấy làm gì thì ABCD..XYZ tùy bạn :v). Ví dụ: __.ts
this.favoriteColorControl.valueChanges.subscribe(
valueColor => {
console.log('Color changed:' + valueColor );
}v. Form Validation
Validation là một phần cần thiết để quản lý bất kì dạng Form nào. Cho dù bạn đang kiểm ra các trường hay truy vấn API for một username nào đó đang tồn tại thì Angular đều cung cấp các validator xây dựng sẵn đủ sức để các bạn có thể tùy biến theo nhu cầu của mình.
- Reactive Forms: Thông qua hàm để validate control.
- Template-Driven Forms: dùng template Directives và nếu có xây dựng riêng hàm validate thì vẫn phải gói trong Custom Validator Directives
Bạn còn có thể dùng RegEx để validate thông qua directive. Ví dụ:
__A.html
<input id="phone" [pattern]="regExpClass.PhoneNum"
[(ngModel)]="model.PhoneNum" required>
__A.ts
import { RegExpClass } from 'app/core/__B';
@Component({
selector: 'app',
templateUrl: './__A.html',
})
export class AComponent {
regExpClass = RegExpClass;
....
}__B.ts
export class RegExpClass {
public static readonly PhoneNum = "^(\\+84|0)[0-9]{8,10}$";
}VI.Mutability
Có sự khách nhau trong việc theo dõi thay đổi ở ví dụ trên trong Input Element favorite color:
- Reactive Form: FormControl instance luôn luôn trả về một giá trị mới khi giá trị control được update.
- Template-Driven Forms: favorite color properties luôn luôn modify giá trị mới của nó.
Q: Okay việc này cho ta thấy gì?
A: <Thật sự thì chỗ này hơi phức tạp mình sẽ update để giải thích rõ hơn sau – Các bạn cứ đọc sơ sơ nắm ý trước>
– Reactive forms giữ data model ở trạng thái ban đầu bằng cách tạo ra nó như một cấu trúc dữ liệu không đổi (immutable data structure). Mội khi thay đổi trigger trên data model, FormControl instance trả về một data model mới chứ không phải update thằng đang có. Việc này cho bạn khả năng theo dõi những thay đổi độc nhất. Nó cũng tuân theo reactive pattern tích hợp với các observable operator (toán tử) để chuyển hóa dữ liệu.
_ Template-driven forms dựa trên khả năng biến đổi với 2-way data binding để update data model trong component khi thay đổi ở template. Bởi vì sẽ không có sự thay đổi độc nhất nào được theo dõi trên data model khi sử dụng 2-way data binding, change dectection sẽ ít hiệu quả hơn tại việc xác định khi update được yêu cầu.
VII. Scalability
Nếu như Form là nhân tố chính trong ứng dụng của bạn thì khả năng mở rộng là điều vô cùng quan trọng. Để có thể tái sử dụng Form Models xuyên suốt các component là điều tiên quyết cần đạt được.
- Reactive forms: cung cấp truy cập low-level API và truy cập đồng bộ tới form model, điều này khiến cho việc mở rộng form dễ dàng hơn.
- Template-driven forms: tập trung vào các ngữ cảnh đơn giản, ít cần đến việc tái sử dụng, nên truy cập low-level APIs trở nên không rõ ràng (abstract away) và ở đây có cung cấp dạng truy cập bất đồng bộ tói form model.
VIII. Lời kết
Mới là phần 1 thôi nhưng cũng có quá nhiều thứ để nói nhỉ ^^ Qua phần đầu này ta cũng nắm được sơ sơ về Form. Tất nhiên cũng còn lại 2-3 phần gì đó trong phần đầu này cần được giải thích rõ hơn nhưng sẽ là những bài thảo luận riêng! Mong anh xem hay subcribe channel và cũng như theo dõi blog đế tiếp tục hành trình cũng mình nhen.
[Carry by: TungTTr – Date: 19/03/2019]
[Update: 26/03/2019 ]

