Angular: Form in Details (Part 1)

Giới thiệu

Form có lẽ là một trong những thành phần quan trọng bậc nhất trong bất kỳ một app web nào. Cho dù ta có thể nhận được những data theo những cách khác nhau từ phía người sử dụng như click, hover,… mặc dù vậy, form vẫn là một phuơng pháp mạnh mẽ và trực quan nhất để ta có thể thu thập tin tức từ phía người sử dụng

Nhìn từ phía giao diện, form có vẻ rất dễ hiểu và rõ ràng: ta có 1 thẻ <input>, người sử dụng điền các tin tức được đề ra, bấm submit, nhưng vì sao xử lý form lại liên tục khá khó khăn và đòi hỏi ta bỏ khá nhiều công sức để xử lý chúng một cách có tác dụng?

Nếu xem xét kỹ hơn chuyện, form có thể rất phức tạp, sau đây là một vài lý do:
– các trường input của form đổi khác data trên cả server và client
– khi đổi khác các giá trị được nhập vào form, những giá trị đổi khác đó cần được phô diễn ở các nơi khác trên trang
– người sử dụng được toàn quyền nhập các giá trị vào form, do vậy, ta cần một vài phuơng pháp để validates các giá trị đó
– giao diện phải hiển thị rõ ràng các lỗi và giải thích tường tận cho người sử dụng
– một vài trường của form phụ thuộc vào giá trị của các trường khác, do đó, ta cần xử lý logic các giá trị này
– tất nhiên, test là một buớc không thể thiếu, mặc dù vậy, ta cần test các form mà không phụ thuộc vào các DOM seleGiám đốc kỹ thuậtr
– …

Một điều đáng mừng cho các bạn dùng Angular, đó là ở version hiện tại (Angular 4), ta được cung ứng một cách khá đầy đủ các công cụ để có thể giải quyết các chuyện đã nêu. Vì vậy, trong bài viết này, ta sẽ đã có lần bước tạo dựng form từ form cơ bản nhất, từ đó tạo dựng các forms khác có sự phức tạp hơn về logic ở các bài tiếp nữa.

FormControls và FormGroups

Đây là 2 đối tượng cơ bản nhất để ta làm việc với form trong Angular

FormControl

FormControl đại diện cho 1 trường input, đây là đơn vị nhỏ nhất của 1 form trong Angular
FormControl chứa các giá trị của trường hiện tại, trạng thái (state) của trường, ví dụ như valid, dirty, hoặc có lỗi hay không.
Để tạo dựng 1 các form, ta tạo nhiều các FormControls này, thêm các metadata và các thành phần logic cho chúng
Tuơng tự với nhiều các thành phần khác trong Angular, ta có một class (FormControl) mà sẽ được gắn vào DOM bằng 1 attributes (formControl), ví dụ:
<input type="text" [formControl]="name"/>
dòng trên sẽ tạo mới một đối tượng FormControl trong phạm vi của form ta đang tạo, cụ thể hơn, mình sẽ đề cập ở phần dưới

FormGroup

hầu như các form đều có nhiều hơn một trường, do vậy, Angular cung ứng cho ta một cách để quản lý các FormControls là FormGroup. Hãy thử xét một hoàn cảnh cho thấy sự rất cần của FormGroup:
Khi form của ta có nhiều trường, ta cần kiểm tra tính hợp lệ của các trường đó. Nếu không có FormGroup, ta buộc phải lặp qua các phần tử mảng FormControls và kiểm tra tính hợp lệ của đã có lần FormControl, khá phức tạp khi mỗi FormControl có thể lại có một cách kiểm tra tính hợp lệ khác nhau.

Chính vì vậy, để giải quyết chuyện này, FormGroup được tạo ra để cung ứng một interface giúp đỡ ta có thể thao tác rất dễ với một tập hợp các FormControls

Cách tạo một FormGroup, rất dễ hiểu:

let personalInfo = new FormGroup({     firstName: new FormControl('Phuong');     lastName: new FormControl('Tran');     zip: new FormControl('100000'); }) 

FormGroup và FormControl có chung một lớp cha (AbstractControl), điều đó có nghĩa, ta cũng có thể kiểm tra statusvalue của personnalInfo như cách đã làm với FormControl:

personalInfo.value // { firstName: "Phuong", lastName: "Tran", zip: "100000"} personalInfo.errors // StringMap<string, any> of errors personalInfo.dirty //false personalInfo.valid // true 

quan tâm rằng, khi ta lấy value của FormGroup, ta nhận được một đối tượng với 1 cặp key-value ứng với đã có lần FormControl, điều này rất hữu ích do ta không cần lặp qua các FormControls để lấy đã có lần giá trị của chúng

tạo dựng form

ta sẽ cùng nhau tạo dựng một forms để có thể có cái nhìn rõ nét hơn về form, mình sẽ giải thích cụ thể ở đã có lần bước trong khi ta làm
Form đầu tiên của ta sẽ rất dễ hiểu, chỉ gồm một label, một trường input và một nút submit mà thôi.

1. Import các thư viện rất cần

Để có thể dùng thư viện forms trong Angular, ta cần import chúng vào trong NgModule

MÌnh xin nói thêm, trong Angular, có 2 kiểu forms mà ta có thể dùng: FormsModuleReactiveFormsModule, trong bài này, mình sẽ ưu tiên giới thiệu FormsModule trước do nó dễ approach hơn và dễ hiểu hơn 1 chút 😀

(Nói thêm một chút về FormsModule, module này cung ứng cho ta một vài template driven directives, hiểu nôm na là ta tạo dựng form dựa vào các phần tử trên DOM)

Để import thư viện, ta vào thư file app.module.ts (mình mặc định các bạn dùng công cụ angular/cli nhé)

import các module rất cần:

import {     FormsModule,     ReactiveFormsModule } from '@angular/forms'; 

imports vào trong @NgModule

@NgModule({     ...     imports: [         ...         FormsModule,         ReactiveFormsModule         ...     ]     ... }) 

Vậy là xong, giờ ta có thể bắt đầu rồi xD

2. @Component Decorator (app.component.ts)

Hiện tại, bạn vẫn dùng các giá trị mặc định cho file này (do ta chưa cần quan tâm đến logic của form)

3. Template (app.component.html)

sau đây là template của form, như mình đã nói, rất dễ hiểu, chỉ input. label và submit thôi, ơ mà khoan, sao nhiều thứ thế?

<div class="container">   <h2 class="text-center">What's your name?</h2>   <form #f="ngForm" (ngSubmit)="onSubmit(f.value)">      <div class="form-group">       <label for="nameInput">Name</label>       <input type="text"              class="form-control"              id="nameInput"               placeholder="Enter your name here"               name="username"               ngModel>     </div>      <button type="submit" class="btn btn-primary">Submit</button>   </form> </div> 

Ok, mình không điêu đâu, cơ mà chấp nhận là nó nhiều thứ thật, giải thích đã có lần phần nhé:

formNgForm

Do ở bước 1, ta đã import FormsModule, điều này sẽ khiến NgForm có sẵn để dùng trên view. Khi ta làm cho 1 directive (ở đây là NgForm) có sẵn trên view, chúng sẽ gắn với bất kỳ element nào trên DOM mà có seleGiám đốc kỹ thuậtr khớp với seleGiám đốc kỹ thuậtr của directive đó

NgForm cũng làm việc như vậy, nó chứa form trong seleGiám đốc kỹ thuậtr của mình, điều đó có nghĩa rằng, khi ta include FormsModule, NgForm sẽ được gắn vào bất kỳ các thẻ <form> nào trên view

Có 2 chức năng quan trọng mà NgForm cung ứng cho ta:
FormGroup có tên ngForm
(ngSubmit) output

Cùng xem xét kỹ hơn template ta đang có:

 <form #f="ngForm"               (ngSubmit)="onSubmit(f.value)"> 

Ở đây, cú pháp #f="ngForm" có thể hiểu: ta tạo 1 alias cho ngForm trên view hiện tại, gán vào biến #f, ngForm có được là từ NgForm directive
ngForm ở đây là một đối tượng kiểu FormGroup, do vậy, ta có thể dùng biến f để lấy giá trị của FormGroup này và đưa vào như 1 tham số cho (ngSubmit)

Chắc hẳn nhiều bạn sẽ thắc mắc: ở trên mình có nói là việc import NgForm sẽ tự động gắn với bất kỳ thẻ <form> nào trên view, vậy vì sao ở dưới lại phải thêm ngForm làm gì?
Okay, nếu ta dùng ngForm như một key của 1 thuộc tính, ta đang khai báo với Angular rằng ta sẽ dùng NgForm trong thuộc tính đó, còn trong hoàn cảnh hiện tại, ta dùng ngForm như một giá trị thuộc tính khi ta gán nó vào 1 biến (#f). Vì vậy, có thể nỏi rằng, giá trị của biểu thức ngForm sẽ được gán vào giá trị biến f
ngForm như đã nói, có sẵn trên element này rồi, ta có thể hiểu thứ đang diễn ra ở đây là: export giá trị của FormGroup ra và ta có thể tham chiếu tới nó ở các vùng khác trên view

tiếp nữa, (ngSubmit)="onSubmit(f.value)" có lẽ không cần giải thích quá nhiều: khi ta submit form, gọi hàm onSubmit trong component, với tham số là f.value

inputngModel

  <div class="form-group">       <label for="nameInput">Name</label>       <input type="text"              class="form-control"              id="nameInput"               placeholder="Enter your name here"               name="username"               ngModel>     </div> 

Cần quan tâm ở input:
label forid của input cần khớp với nhau (theo W3C)

NgModel directive có seleGiám đốc kỹ thuậtr là ngModel, điều đó có nghĩa rằng, ta có thể gắn NgModel với input một cách rất dễ qua cú pháp: ngModel="something", mặc dù vậy, ở đây ta lại không đặt một giá trị nào cho attribute ngModel cả, hoàn cảnh này, sẽ phát sinh một vài thứ như sau:
– one-way data binding
– tạo FormControl trên form này, với tên là username (do thuộc tính name có giá trị username

NgModel sẽ tạo 1 FormControl và tự động được thêm vào FormGroup cha (ở đây chính là <form></form>), tiếp nữa gắn DOM element vào FormControl này

***NModel và nModel
– NgModel: tên class, tham chiếu đến kiểu của 1 đối tượng nào đó
– ngModel: là seleGiám đốc kỹ thuậtr của directives, chỉ có thể dùng trên view

4. Implement chức năng Submit (app.component.ts)

onSubmit(form: any): void {     console.log('you submitted value:', form); } 

Không có gì khỏ hiểu cả :D, mình sẽ để đây và không nói gì 😀

Sau khi viết xong, các bạn có thể kiểm tra thành quả 😀 :

Kết luận

Phew! một form dễ hiểu mà mất nhiều công sức nhỉ?

Mình đồng ý với ý kiến đó, mặc dù vậy, nó cũng giống như các huấn luyện viên khi chỉ dẫn cho các cầu thủ vậy: mô tả kỹ thuật rất cụ thể, mất nhiều thời gian, nhưng khi cầu thủ làm kỹ thuật lại chỉ mất có vài giây 😀

vì vậy, bài viết này mình chấp nhận khá dài, nhưng khi các bạn đã nắm được những gì thực sự diễn ra dưới những dòng ngắn đó, bạn sẽ tiến nhanh hơn nhiều ở những phần sau, và thêm phần cảm ơn các kỹ sư của Angular đã tạo ra 1 framwork tuyệt vời đến thế nào

Chúc các bạn thành công, hẹn gặp lại các bạn ở những phần sau 🙂

Nguồn viblo.asia