SSV
+1

Implement Form trong Angular 2 - cơ bản và hơn thế nữa - Part 1

Đây là bài dịch, bài gốc mời các bạn xem ở đây : http://restlet.com/company/blog/2016/02/11/implementing-angular2-forms-beyond-basics-part-1/

Trong bài viết lần này, tôi sẽ trình bày về việc implement một form để cập nhật data của một Web API được host trên Restlet Cloud. Tiếp nối bài viết Implement một trang Angular với Web API được cung cấp bởi dịch vụ APISpark, ngày hôm nay chúng ta sẽ tập trung vào việc làm thế nào để cập nhật một thành phần cụ thể, là một công ty, bằng cách sử dụng một request dạng PUT tuân theo chuẩn RESTful với resource (tài nguyên) là một công ty (company).

Chúng ta sẽ giữ cấu trúc dữ liệu đơn giản để tránh phải xử lý những vấn đề phức tạp không cần thiết khi tạo form. Dưới đây là các thuộc tính của một công ty:

Tạo form

Form là cách chính quy nhất để cập nhật dữ liệu trong một trang HTML. Trong phần đầu tiên, chúng ta sẽ cùng tìm hiểu cách để tạo ra các thành phần của form bằng HTML, làm đẹp bằng Bootstrap, và thêm những tương tác bằng Angular 2.

Định nghĩa form

Phần này sẽ mô tả cách để tạo ra một form bằng cách sử dụng Twitter Bootstrap 3. Ngoài bootstrap, chúng ta cũng có thể sử dụng Material với một giao diện khá hiện đại cho các ứng dụng web.

Bạn có thể biết sự khác nhau giữa các thẻ dùng để tạo ra form trong HTML như thế nào ( vd như là <form>, <input>, <select>, <textarea>). Bootstrap 3 cung cấp một tập các class CSS để làm cho các thẻ đó trở nên lung linh hơn và dễ sắp đặt hơn.

Đây là một ví dụ về một form:

<form class="form-horizontal">
  <!-- Name field -->
  <div class="form-group form-group-sm">
    <label for="name" class="col-sm-3 control-label">Name</label>
    <div class="col-sm-8">
      <input type="text" name="name"/>
    </div>
  </div>

  <!-- Submit button -->
  <div class="form-group">
    <div class="col-sm-offset-3 col-sm-8">
      <button type="submit" class="btn btn-primary">Save</button>
    </div>
  </div>
</form>

Dưới đây là kết quả của đoạn code trên, một form khá đẹp mắt tuy nhiên vẫn ở dạng tĩnh (static) - tức là chưa có tương tác cụ thể :

Tiếp theo, chúng ta sẽ cùng xem cách Angular 2 hỗ trợ các tương tác vào form như thế nào bằng cách thêm một vài xử lý validation và hiển thị thông báo lỗi nếu có.

Trạng thái của form

Giống như phiên bản đầu tiên của mình, Angular 2 cũng quản lý các trạng thái của form nói chung và từng trường (từng field) nói riêng. Việc quản lý trạng thái này cho phép chúng ta có thể kiểm xem từng trường, một nhóm nhiều trường, hoặc cả form có dữ liệu hợp lệ hay không, dựa trên những validation cụ thể. Angular 2 cung cấp một phương thức để định nghĩa các trạng thái và trạng thái bên trong cho form một cách hết sức linh hoạt.

Cách tiếp cận đầu tiên là định nghĩa toàn bộ trong template của component.

Định nghĩa Form bằng Template

Trong trường hợp này, chúng ta sẽ không cần thêm bất kỳ dòng code nào trong phần định nghĩa component. Mọi thứ có thể cấu hình ở trong template html của component với sự trợ giúp của NgControlName directive. Việc này cho phép chúng ta định nghĩa một control (thẻ html tạo form) kèm theo tên cho một field cụ thể. Control sau đó có thể được gán cho một biến local. Trong trường hợp này, giá trị của biến phải là "ngForm". Biến local này sau đó sẽ được dùng để kiểm tra xem field có hợp lệ hay không.

<input #name="ngForm" ngControl="name" [(ngModel)]="company.name"/>

Trong trường hợp muốn sử dụng control này cho cả form hoặc một nhóm nhiều trường, chứ ko đại diện cho một trường cụ thể nữa, thì chúng ta chỉ cần tạo một biến local ở cấp tương ứng. Trong trường hợp đó, trạng thái của control được gọi là state container, và sẽ được cập nhật dựa theo các trạng thái của các control mà nó bao gồm.

<form #companyForm="ngForm">
  <input #name="ngForm" ngControl="name" [(ngModel)]="company.name"/>
</form>

Đây là cách định nghĩa control inline, và sẽ trở nên hạn chế nếu chúng ta muốn sử dụng thêm những validation tuỳ chỉnh (custom validation).

Sử dụng control đã tồn tại

Angular 2 cũng cho phép chúng ta có thể tạo các control để liên kết với các thành phần của form một cách thủ công. Cách tiếp cận này sẽ trở nên hữu dụng khi xử lý validation của field một cách độc lập. Và hiển nhiên là nó sẽ không liên kết với toàn bộ form. Vì thế sẽ có trường hợp mà form vẫn hợp lệ trong khi có 1 field nào đó không hợp lệ.

Vì lý do đó mà Angular đã cung cấp thêm một class có tên là FormBuilder để định nghĩa các validation cho từng field của form. Và chúng ta cũng có thể định nghĩa một group các fields, mà khi một field không hợp lẹ sẽ khiến cả group cũng trở nên không hợp lệ.

constructor(builder:FormBuilder) {
  this.companyForm = builder.group({
    name: ['', Validators.required]
  });
}

Các thành phần tương ứng có thể được tham chiếu trong template của thông qua việc sử dụng 2 directive là FormModelFormControl, như mô tả dưới đây:

<form [ngFormModel]="companyForm">
  <input [ngFormControl]="companyForm.controls.name" [(ngModel)]="company.name"/>
</form>

Giờ chúng ta đã thiếp lập xong form và các trạng thái của field, chúng ta cũng có thể thiết lập các luật cho việc validate các field của form dựa trên các field trong controller.

Form Validation

Một số Validation đã được cung cấp sẵn bởi Angular 2:

  • required: Field không được để trống
  • minLength: độ dài tối thiểu của field
  • maxLength: độ dài tối đa của field

Như bạn đã thấy, danh sách của các validator mặc định là rất ít, tuy nhiên việc tạo mới một validator theo nhu cầu thực tế trong Angular 2 là hết sức dễ dàng bởi chúng ta sẽ chỉ cần implement một hàm là đủ. Với form cho đối tượng công ty chúng ta đang thực hiện, ta sẽ cần phải kiểm tra xem người dùng có nhập zip code hợp lệ trong địa chỉ hay không, v.d phải là một số với 5 ký tự. Chúng ta có thể sử dụng regular expression để kiểm tra việc này. Nếu giá trị zip code là đúng, hàm validate chỉ đơn giản trả về null. Trong trường hợp ngược lại, sẽ trả về một object chứa key của validator với một object con được mô tả như dưới đây:

function zipCodeValidator(control) {
  var valid = /^\d{5}$/.test(control.value);
  return !valid ? { invalidZip: true }: null;
}

Để đăng ký hàm validation này, chúng ta cần tự tạo ra form controls bằng cách tiếp cận thứ 2, dùng FormControl và FormGroup. Nếu chúng ta chỉ sử dụng validator này, thì chỉ cần đặt nó vào trong thành phần thứ 2 của mảng mà chúng ta dùng để định nghĩa form controls. Nếu có nhiều hơn một validator, chúng ta cần sử dụng hàm compose của lớp Validator như sau :

this.companyForm = builder.group({
  name: ['', Validators.compose([ Validators.required, zipCodeValidator ])]
});

Giờ chúng ta đã biết cách tạo ra các validator cho các trường như mong muốn. Tiếp theo chúng ta sẽ sử dụng các trạng thái của form để hiển thị lỗi cho các field không hợp lệ.

Tận dụng trạng thái của form

Trạng thái của form (form state) cung cấp cho chúng ta khả năng nâng cao trải nghiệm người dùng với việc có thể thông báo về những trường không hợp lệ một cách hiệu quả.. Như đã nói trước đó, chúng ta sẽ tận dụng Bootstrap để tạo ra một form thật đẹp.

Nếu một field bị lỗi validation, chúng ta chỉ đơn giản là thêm class has-error đến khối của field tương ứng và hiển thị thông báo lỗi ở trong một khu vực có class là help-blocktext-danger như ở dưới đây:

<div class="form-group form-group-sm [ngClass]="{'has-error':!companyForm.controls.name.valid}">
  <label for="for" class="col-sm-3 control-label">Name</label>
  <div class="col-sm-8">
    <input #name="ngForm" ngControl="name" [(ngModel)]="company.name" required/>
    <span *ngIf="!companyForm.controls.name.valid" class="help-block text-danger">
      <span *ngIf="companyForm.controls.name.errors.required">The field is required</span>
    </span>
  </div>
</div>

Màu sắc của khối nhập tên khi bị lỗi sẽ đổi sang màu đỏ như hình dưới đây:

FormControl cũng cho phép chúng ta tương tác với việc update dữ liệu thông qua việc tận dụng sức mạnh của mô hình lập trình Reactive. Chúng ta sẽ bàn về vấn đề này trong các bài tiếp theo.

Như bạn đã thấy, chúng ta đã implement những thao tác cơ bản nhất với form bằng Bootstrap 3 và validator của angular. Đây là một khởi đầu tốt, và chúng ta sẽ bàn tiếp đến việc tận dụng tối đa khả năng của Angular 2 trong việc đơn giản hoá form, xoá các code lặp và áp dụng những tương tác một cách thông suốt. Hẹn gặp các bạn ở phần 2 của series.


All Rights Reserved