Daily Archives: 17/01/2017

Angular – Kiến trúc

Một ứng dụng Angular được xây dựng từ 8 thành phần sau đây: Module, Component, Template, Metadata, Data Binding, Directive, Service, Dependency Injection.

Module

Mỗi ứng dụng Angular được gọi là một module và bản thân Angular có riêng một module dùng để quản lý các module khác có tên là Root Module hay NgModule. Root Module thường được đặt tên là AppModule, ngoài root ra thì tùy ứng dụng mà sẽ có thêm các module khác, chúng ta sẽ tìm hiểu về root module trong bài sau.

Chúng ta khai báo một module bằng cách dùng từ khóa @NgModule. Các từ khóa như @NgModule này là các hàm dùng để chỉnh sửa các lớp của Javascript. Bên trong từ khóa @NgModule chúng ta khai báo các tham số sau đây:

  • declarations: tên lớp view thuộc về module này
  • exports: danh sách tên các module hoặc component có thể sử dụng module này
  • imports: tên các module sẽ được dùng từ module này
  • providers: tên các service sẽ được dùng từ module này, chúng ta sẽ tìm hiểu về service sau
  • bootstrap: tên lớp view dành cho root module, chỉ có root module mới thiết lập tham số này

Đây là một đoạn code module trong file có tên app.module.ts đơn giản như sau:

import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
@NgModule({
    imports:      [ BrowserModule ],
    providers:    [ Logger ],
    declarations: [ AppComponent ],
    exports:      [ AppComponent ],
    bootstrap:    [ AppComponent ]
})
export class AppModule { }

Để gọi module trên thì chúng ta code như sau:

import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
import { AppModule } from './app.module';

platformBrowserDynamic().bootstrapModule(AppModule);

Component

Component có chức năng điều khiển việc hiển thị, tức là điều khiển View, vậy bạn có thể hình dung Component chính là một Controller trong mô hình MVC…v.v Ví dụ một đoạn code component:

export class HeroListComponent implements OnInit {
    heroes: Hero[];
    selectedHero: Hero;

    constructor(private service: HeroService) { }

    ngOnInit() {
        this.heroes = this.service.getHeroes();
    }

    selectHero(hero: Hero) { this.selectedHero = hero; }
}

Template

Template là một đoạn code HTML để component dựa vào đó mà hiển thị trên màn hình. Ví dụ:

<h2>Hero List</h2>
<p><i>Pick a hero from the list</i></p>
<ul>
    <li *ngFor="let hero of heroes" (click)="selectHero(hero)">
        {{hero.name}}
    </li>
</ul>
<hero-detail *ngIf="selectedHero" [hero]="selectedHero"></hero-detail>

Ngoài các thẻ HTML thông thường như <h2>, <p> thì còn có những thẻ và thuộc tính đặc biệt như *ngFor, {{hero.name}}, (click), [hero]<hero-detail>, đây là cú pháp template của Angular.

Metadata

Metadata (siêu dữ liệu) là những thông tin giúp Angular xử lý các lớp.

Trong đoạn code ví dụ về Component ở trên, đó chỉ là một lớp bình thường viết bằng TypeScript, không có sự xuất hiện của Angular trong này. Muốn Angular hiểu được đó là một lớp dành cho Angular thì chúng ta phải khai báo metadata. Ví dụ:

@Component({
    moduleId: module.id,
    selector: 'hero-list',
    templateUrl: 'hero-list.component.html',
    providers: [ HeroService ]
})
export class HeroListComponent implements OnInit {
    /* . . . */
}

Trong đó @Component là từ khóa bắt đầu định nghĩa metadata, phần định nghĩa lớp ngay sau phần metadata này là lớp component của metadata trên. Bên trong chúng ta khai báo một số thông tin cho Angular như moduleId, selector, templateUrl, providers. Chúng ta sẽ tìm hiểu về chúng sau.

Data Binding

Data Binding tức là lấy dữ liệu từ model/controller đổ vào view. Trong đoạn code ví dụ về template trên có những dòng data binding như sau:

<li>{{hero.name}}</li>
<hero-detail [hero]="selectedHero"></hero-detail>
<li (click)="selectHero(hero)"></li>

Data binding trong Angular là 2 chiều, tức là chúng ta có thể nhập dữ liệu từ view vào model/controller.

 
<input [(ngModel)]="hero.name">

Directive

Directive (chỉ thị) là một lớp và có phần khai báo metadata là @Directive. Thường thì directive sẽ nằm trong một element – hay thẻ của HTML giống như một thuộc tính bình thường.

Có 2 loại directive là structuralattribute.

Các structural directive có chức năng gán dữ liệu theo một quy tắc nào đó.

<li *ngFor="let cus of customer"></li>
<customer *ngIf="selectedCustomer"></customer>

Trong đoạn code trên thì *ngFor*ngIf là các structural directive.

Các attribute directive có chức năng hiển thị dữ liệu một cách trực tiếp.

<input [(ngModel)]="hero.name">

Trong đoạn code trên thì ngModel là một attribute directive.

Service

Service là các lớp có khả năng thực hiện một số chức năng thường dùng, nói đơn giản thì chúng giống như thư viện vậy. Một số service phổ biến là: logging service, data service, message bus, tax calculator, application configuration.

Ví dụ lớp Logger cho phép chúng ta in các đoạn code báo lỗi, cảnh báo…v.v:

export class Logger {
    log(msg: any) { console.log(msg); }
    error(msg: any) { console.error(msg); }
    warn(msg: any) { console.warn(msg); }
}

Dependency injection

Dependency là các lớp/module/service được dùng thêm, Dependency injection là khả năng cho phép tạo các đối tượng lớp có đầy đủ các lớp/module/service được dùng thêm đó. Chẳng hạn như chúng ta có phương thức constructor() như sau:

constructor(private service: HeroService) { }

Tham số private service: HeroService có nghĩa là lớp này cần dùng một service có tên HeroService.

Angular có riêng một vùng bộ nhớ để lưu trữ các dependency đã được gọi, khi một module/component nào cần dùng service nào, Angular sẽ tìm trong vùng bộ nhớ đó xem có không, nếu không có thì Angular sẽ tạo một đối tượng của dependency đó và đưa vào bộ nhớ rồi trả về cho lớp đã gọi.

Khi chúng ta xây dựng root module thì chúng ta phải khai báo các dependency trong tham số providers, có như thế Angular mới có thể tìm được.

providers: [
    BackendService,
    HeroService,
    Logger
],

Hoặc khai báo ở phần @Component:

@Component({
    moduleId: module.id,
    selector: 'hero-list',
    templateUrl: 'hero-list.component.html',
    providers: [ HeroService ]
})