Bắt đầu với Angular 2 từng bước một: 5 - Forms và Validation

Cập nhật ngày: 26/04/2024 - Đã có 1626 lượt xem bài viết này!
Bắt đầu với Angular 2 từng bước một: 5 - Forms và Validation
Đây là bài thứ 5 trong loạt bài viết: "Bắt đầu với Angular 2 từng bước một". Ở bài trước bạn đã học về Angular 2 routing và trong bài này bạn sẽ học về cách tạo các form và validate form trong Angular 2.

Bắt đầu với Angular 2 từng bước một: 5 - Forms và Validation

Đây là bài thứ 5 trong loạt bài viết: "Bắt đầu với Angular 2 từng bước một".
Ở bài trước bạn đã học về Angular 2 routing và trong bài này bạn sẽ học về cách tạo các form và validate form trong Angular 2.

Code mẫu

Bạn có thể tải toàn bộ code mẫu ở GitHub repo.

Ứng dụng của chúng ta cho tới thời điểm này

Hiện tại, chúng ta đã phát triển một ứng dụng Angular 2 nhỏ. Với 2 component PeopleListComponent hiển thị một danh sách các nhân vật và PersonDetailComponent hiển thị thông tin chi tiết về nhân vật mà bạn chọn.

Chúng ta sử dụng Angular 2 routing để điều hướng giữa 2 view, danh sách các nhân vật là view mặc định và render ngay sau khi ứng dụng khởi tạo.

Chúng ta muốn lưu dữ liệu của mình!

Cho tới thời điểm này, chúng ta chỉ đọc thông tin và đã cảm thấy điều này thật tẻ nhạt! Chúng ta muốn có thể viết và lưu dữ liệu của mình.

Để làm được điều đó chúng ta cần chuyển đổi PersonDetailComponent thành một form và thêm một vài validation để đảm bảo thông tin chúng ta lưu là chính xác.

Khi hoàn thành, form chi tiết của chúng ta sẽ giống như thế này:
 

 

Trước hết! Chúng ta cần thêm module Forms tới ứng dụng của mình!

Chúng ta sẽ cần import FormsModule trong app.module.ts:

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

Và chỉ định nó như một dependency thông qua thuộc tính imports:

@NgModule({
  imports: [ BrowserModule, routing, FormsModule ], // here!
  declarations: [ AppComponent, PeopleListComponent, PersonDetailsComponent],
  bootstrap: [ AppComponent ]
})
export class AppModule { }

Toàn bộ app.module.ts sẽ trông như thế này:

import { NgModule }      from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { FormsModule } from '@angular/forms';

import { routing } from './app.routes';

import { AppComponent }  from './app.component';
import { PeopleListComponent } from './people-list.component';
import { PersonDetailsComponent } from './person-details.component';

@NgModule({
  imports: [ BrowserModule, routing, FormsModule ],
  declarations: [ AppComponent, PeopleListComponent, PersonDetailsComponent],
  bootstrap: [ AppComponent ]
})
export class AppModule { }

Sử dụng một file template thay vì inline template

Điều đầu tiên chúng ta làm, là tách template của PersonDetailsComponent thành một file riêng.

Làm thế nào chúng ta có thể sử dụng một file template thay vì inline template?

Decorator @Component có thuộc tính tempalteUrl ngoài thuộc tính template chúng ta đã sử dụng. Sử dụng thuộc tính này để nói với Angular 2 file HTML template chúng ta sẽ sử dụng cho component. Hãy tách template của PersonDetailsComponent vào file person-details.component.ts:
 

You selected: {{person.name}}

Description

{{person.name}} weights {{person.weight}} and is {{person.height}} tall.

Và cập nhật PersonDetailsComponent:

// some imports

@Component({
  selector: 'person-details',
  templateUrl: 'app/person-details.component.html',    // <=== update this
})
export class PersonDetailsComponent implements OnInit {
    // some code...
}

Một Form cơ bản trong Angular 2

Bây giờ, như đã đề cập, hãy thêm một HTML form tới view của chúng ta. 

Chúng ta cập nhật template như thế này:


You selected: {{person.name}}

Description

{{person.name}} weights {{person.weight}} and is {{person.height}} tall.

Ok, hiện tại form chưa hiển thị thông tin gì. Hãy sử dụng một vài thứ chúng ta đã học như event và property binding để hiển thị thông tin. Chúng ta có thể làm như sau:

Thuộc tính [value] để liên kết thuộc tính person.name của component tới phần tử input, và chúng ta sử dụng (change) event binding để cập nhật thuộc tính name của person

Có một vài thứ khá lạ trong đoạn code trên, đó là #name trong phần tử input. Angular 2 gọi là một biến template cục bộ (template local variable) và thường được sử dụng trong template bên ngoài code của component.

Trong trường hợp này, #name tham chiếu tới chính phần tử input. Đó là lý do tại biểu thức person.name = name.value giúp cập nhật tên của person khi chúng ta thay đổi giá trị của input.

NgModel và Angular 2 two-way data binding.

ngModel data binding rất giống ng-model của Angular 1, nó giúp chúng ta thành lập two-way data binding giữa component và template. Two-way data binding đồng bộ hóa các thay đổi cả ở component và template.

Cú pháp sẽ như thế này:

Trước khi tiếp tục hãy xem xét đoạn code này một chút [(ngModel)]="person.name" từng phần một.

Chúng ta đã học event bindings liên kết một chiều từ template tới component và biểu diễn bằng dấu ngoặc tròn (). Chúng ta cũng đã học property bindings liên kết một chiều từ component tới template biểu diễn bằng dấu ngoặc vuông []. Vì thế, liên kết 2 chiều tương đương với event bindings cộng property bindings và bạn có cú pháp [(ngModel)].

Bây giờ, chúng ta sẽ tạo liên kết 2 chiều cho mỗi thuộc tính của nhân vật (trừ id):


You selected: {{person.name}}

Description

{{person.name}} weights {{person.weight}} and is {{person.height}} tall.

Nếu bạn gõ lệnh npm start bạn có thể thấy bất kỳ khi nào bạn thay đổi giá trị trong thẻ input, các thay đổi sẽ được hiển thị trong phần description.

Tổng kết về Angular 2 data bindings

Angular 2 hỗ trợ các kiểu liên kết dữ liệu (data bindings):

  • interpolation: liên kết dữ liệu một chiều (one-way data binding) từ component tới template. Giúp bạn hiển thị thông tin từ component tới template. VD: {{person.name}}.
  • property bindings: liên kết dữ liệu một chiều (one-way data binding) từ component tới template. Giúp bạn liên kết dữ liệu từ component tới template. VD [src]="person.imageUrl".
  • event bindings: liên kết dữ liệu một chiều (one-way data binding) từ template tới component. Giúp bạn liên kết các sự kiện ở template tới component. VD: (click)="selectPerson(person)".
  • [(ngModel)]: liên kết dữ liệu 2 chiều (two-way data binding) từ component tới template và ngược lại. VD: [(ngModel)]="peson.name".

Thêm validation tới Form

Bây giờ hãy thêm một vài validation để đảm bảo rằng dữ liệu chúng nhập là hợp lệ trước khi lưu chúng.

Chúng ta sẽ thêm vào trường name thuộc tính required, sau đó sẽ hiển thị thông báo lỗi bất kỳ khi nào trường name trống và chúng ta sẽ cho phép hoặc vô hiệu hóa nút submit form dựa trên tính hợp lệ của các thẻ input trong form.

Chúng ta sẽ theo dõi sự thay đổi và tính hợp lệ của thẻ input trong Angular 2 thông qua directive ngModel. Bằng các sử dụng directive này với một thẻ input chúng ta có thể có được các thông tin về việc người sử dụng có hoặc không làm  một thứ gì đó, đã thay đổi hoặc chưa thay đổi giá trị và thậm chí nếu nó không hợp lệ.

Hãy thêm required tới thẻ input name:


Angular 2 sử dụng thuộc tính name để xác định một thẻ input cụ thể và theo dõi sự thay đổi và tính hợp lệ của nó.

Cách dễ dàng nhất để xem Angular 2 theo dõi các thay đổi của thẻ input là xem cách nó thêm hoặc xóa các class vào thẻ input dựa trên trạng thái của thẻ input.

Hãy thêm đoạn code sau để hiển thị thuộc tính className của thẻ input và mở trình duyệt, bạn sẽ thấy các class khác nhau được thêm vào thẻ input thay đổi theo tương tác của bạn:



input "name" class is: {{ name.className }}

Nếu bạn chưa làm gì, các class sẽ là: ng-untouched, ng-pristine và ng-valid

Click vào bên trong sau đó là bên ngoài thẻ input nó sẽ được đánh dấu là visited và có class là ng-touched

Nhập một vài thứ nó sẽ được đánh dấu là dirty và có class ng-dirty

Xóa toàn bộ nội dung nó sẽ được đánh dấu là invalid và có class là ng-invalid.

Chúng ta có thể tận dụng tính năng này để thêm một vài css tới các thẻ input khi chúng hợp lệ hoặc không hợp lệ. Bạn có thể cập nhập styles.css như sau:

.ng-valid[required] {
  border-left: 5px solid #42A948; /* green */
}

.ng-invalid {
  border-left: 5px solid #a94442; /* red */
}

File css này đã được liên kết trong index.html. Nó là styles áp dụng cho toàn bộ ứng dụng của bạn.

Trở lại PersonDetailsComponent template, bây giờ chúng ta muốn hiển thị một thông báo lỗi bất kỳ khi nào người dùng không nhập tên. Chúng ta có thể làm điều đó bằng cách tạo một biến template cục bộ (local template variable) và thiết lập giá trị của nó là ngModel như thế này:


Bây giờ, name giữ các giá trị của directive ngModel, chúng ta có thể truy cập các thuộc tính của nó và kiểm tra trạng thái của thẻ input là hợp lệ hay không hợp lệ. Chúng ta có thể sử dụng thông tin đó để hiển thị hoặc ẩn thông báo lỗi:



Name is required my good sir/lady!

Thêm style sau vào file styles.css:

.error {
  padding: 12px;
  background-color: rgba(255, 0, 0, 0.2);
  color: red;
}

Chú ý cách sử dụng property binding, chúng ta có thể liên kết mọi biểu thức tới thuộc tính DOM [hidden]. Trong trường hợp này chúng ta chỉ ẩn thông điệp khi ngModel nói cho chúng ta là thẻ input là valid (hợp lệ) hoặc pristine (trạng thái khi người sử dụng chưa chỉnh sửa input).

Bước tiếp theo chúng ta sẽ thực sự lưu các thay đổi. Hãy thêm một nút submit vào cuối form:

Và chúng ta cần vô hiệu hóa khi form không hợp lệ.

Để làm điều đó chúng ta sẽ tạo một biến template cục bộ (local template variable) khác #personForm để truy cập tới form thực sự thông qua directive ngForm. Sau đó, chúng ta sẽ sử dụng biến này để vô hiệu hóa nút submit khi form không hợp lệ:

Cuối cùng, chúng ta thiết lập sự kiện submit trong form để có thể lưu thông tin chi tiết của nhân vật bất kỳ khi nào chúng ta submit form:

Toàn bộ template trông sẽ như thế này:


You selected: {{person.name}}

Description

{{person.name}} weights {{person.weight}} and is {{person.height}} tall.

Name is required my good sir/lady!

Chúng ta chỉ cần cập nhật PersonDetailsComponent để có thể sử lý sự kiện submit:

// imports 

@Component({
  selector: 'person-details',
  templateUrl: 'app/people/person-details.component.html'
})
export class PersonDetailsComponent implements OnInit {
    // codes...

    savePersonDetails(){
        alert(`saved!!! ${JSON.stringify(this.person)}`);
    }
}

Bây giờ, bạn có kiểm tra tất cả những thứ đã làm trong trình duyệt: click vào Luke, thay đổi tên của anh ấy và lưu lại, bạn sẽ thấy các thay đổi của bạn trong một hộp thoại.

Tiếp theo hãy cập nhật component của chúng ta để có thể lưu thông tin đã thay đổi với sự trợ giúp của PeopleService.

Lưu thông tin

Chúng ta sẽ thêm phương thức save trong PeopleService để lưu các thay đổi về nhân vật được chọn.

Bắt đầu bằng cách cập nhật PersonDetailsComponent:

// etc
export class PersonDetailsComponent implements OnInit {
    savePersonDetails(){
      this.peopleService.save(this.person);
    }

Sau đó là cập nhật PeopleService với phương thức save:

import { Injectable } from '@angular/core';
import { Person } from './person';

const PEOPLE : Person[] = [
      {id: 1, name: 'Luke Skywalker', height: 177, weight: 70, profession: ''},
      {id: 2, name: 'Darth Vader', height: 200, weight: 100, profession: ''},
      {id: 3, name: 'Han Solo', height: 185, weight: 85, profession: ''},
    ];

@Injectable()
export class PeopleService{

  getAll() : Person[] {
    return PEOPLE.map(p => this.clone(p));
  }
  get(id: number) : Person {
    return this.clone(PEOPLE.find(p => p.id === id));
  }
  save(person: Person){
    let originalPerson = PEOPLE.find(p => p.id === person.id);
    if (originalPerson) Object.assign(originalPerson, person);
    // saved muahahaha
  }

  private clone(object: any){
    // hack
    return JSON.parse(JSON.stringify(object));
  }
}

Bạn có thể chú ý đến phương thức clone. Mục đích của nó là để tránh chia sẻ cùng một object giữa các component khác nhau.

Đâu là sự khác biệt giữa ngModel và ngForm?

Nếu bạn giống tôi, bạn thường nhầm lẫn một chút giữa ngModel và ngForm. Vì thế hãy tóm tắt về chúng:

  • ngModel giúp bạn theo dõi trạng thái và tính hợp lệ của các thẻ input.
  • ngModel thêm các class tới các thẻ input dựa vào trạng thái của chúng.
  • Bất kỳ khi nào bạn thêm directive ngModel tới một thẻ input Angular 2, bạn cần đăng ký nó sử dụng tên bạn cung cấp (nhớ name="name") với directive ngForm Angular 2 tự động đính kèm nó với phần tử form.
  • Sử dụng #name="ngModel" trong một phần tử input tạo ra một biến template cục bộ (local template variable) và gán directive ngModel tới nó. Bạn có thể sử dụng biến này để truy cập tới các thuộc tính của directive ngModel như valid, pristine, touched, ...
  • Angular 2 đính kèm một directive NgForm tới mọi phần tử form
  • Directive ngForm chứa một tập hợp các điều khiển tạo ra bằng cách sử dụng directive ngModel.
  • Directive ngForm cung cấp thuộc tính form.valid giúp bạn biết tất cả điều khiển trong một form là hợp lệ hay không.

Thêm một thẻ select trong Angular 2

Hãy thử thêm một thẻ select với Angular 2 để lựa chọn profession của nhân vật được chọn.

Chúng ta sẽ bắt đầu với việc thêm profession tới interface Person:

export interface Person {
  id: number;
  name: string;
  height: number;
  weight: number;
  // it is optional because I know it
  // doesn't exist in the API that we will
  // consume in the next exercise :)
  profession?: string;
}

Sau đó cập nhật PersonDetailsComponent để bao gồm tất cả các professions có sẵn:

export class PersonDetailsComponent implements OnInit {
    professions: string[] = ['jedi', 'bounty hunter', 'princess', 'sith lord'];
    // other code
}

Và cuối cùng cập nhật PersonDetailsComponent template bao gồm phần tử select:

  
  

 

Bạn đang muốn tìm kiếm 1 công việc với mức thu nhập cao.
✅ Hoặc là bạn đang muốn chuyển đổi công việc mà chưa biết theo học ngành nghề gì cho tốt.
✅ Giới thiệu với bạn Chương trình đào tạo nhân sự dài hạn trong 12 tháng với những điều đặc biệt mà chỉ có tại IMIC và đây cũng chính là sự lựa chọn phù hợp nhất dành cho bạn:
👉 Thứ nhất: Học viên được đào tạo bài bản kỹ năng, kiến thức chuyên môn lý thuyết, thực hành, thực chiến nhiều dự án và chia sẻ những kinh nghiệm thực tế từ Chuyên gia có nhiều năm kinh nghiệm dự án cũng như tâm huyết truyền nghề.
👉 Thứ hai: Được ký hợp đồng cam kết chất lượng đào tạo cũng như mức lương sau tốt nghiệp và đi làm tại các đối tác tuyển dụng của IMIC. Trả lại học phí nếu không đúng những gì đã ký kết.
👉 Thứ ba: Cam kết hỗ trợ giới thiệu công việc sang đối tác tuyển dụng trong vòng 10 năm liên tục.
👉 Thứ tư: Được hỗ trợ tài chính với mức lãi suất 0 đồng qua ngân hàng VIB Bank.
👉  Có 4 Chương trình đào tạo nhân sự dài hạn dành cho bạn lựa chọn theo học. Gồm có:
1)  Data Scientist full-stack
2)  Embedded System & IoT development full-stack
3)  Game development full-stack
4)  Web development full-stack 
✅ Cảm ơn bạn đã dành thời gian lắng nghe những chia sẻ của mình. Và tuyệt vời hơn nữa nếu IMIC được góp phần vào sự thành công của bạn. 
✅ Hãy liên hệ ngay với Phòng tư vấn tuyển sinh để được hỗ trợ về thủ tục nhập học.
✅ Chúc bạn luôn có nhiều sức khỏe và thành công!

Tham khảo các khóa đào tạo nhân sự qua danh mục