Bài viết gồm 2 phần, mở đầu cho series giới thiệu về kiến trúc ứng dụng Angular 2. Để xem phần 2, các bạn có thể vào [link](https://viblo.asia/euclid/posts/EyORkbyQGqB) này. ## Đôi điều về Angular2 - Hiểu đơn giản là một framework để giúp lập trình viên xây dựng các ứng dụng client với HTML và JavaScript/Typescript ... - Việc render HTML templates sẽ được thực hiện bởi Angularized-markup, việc cần làm của một developer là viết các component để quản lý template, thêm mới các xử lý logic bên trong các service, đóng gói component và service thành một module. - Một ứng dụng Angular2 được chạy dựa trên việc bootstrapping root module vào Angular's bootstrapper. ![](https://viblo.asia/uploads/a1b02db2-8d40-425f-91e8-6a6193547b5d.png) Dựa vào sơ đồ kiến trúc trên ta có thể liệt kê ra 8 thành phần chính tạo ra một ứng dụng Angular 2: - Module - Component - Template - Metadata - Data Binding - Service - Directive - Dependency Injection ## Modules ![](https://viblo.asia/uploads/523f82f0-b104-431e-a5af-acb7bcc8e83c.png) Angular apps sử dụng hệ thống module, hay còn gọi là *Angular modules* hoặc *NgModules*. Trong bài viết này chủ yếu là giới thiệu các modules; Để tìm hiểu sâu hơn xin vui lòng tham khảo[ Angular modules](https://angular.io/docs/ts/latest/guide/ngmodule.html). Mỗi ứng dụng Angular đều phải có ít nhất một Angular module class, là *root module*, hay còn được đặt tên theo quy ước là *AppModule*. Trong các ứng dụng nhỏ, đôi khi chỉ có duy nhất 1 module chính là *root module* , tuy nhiên ở hầu hết các ứng dụng, còn có thêm nhiều feature modules. Dù là *root* hay là *feature* module, thì mỗi class đều có `@NgModule` decorator. > Decorators là design pattern thường được dùng để thay đổi hành vi, chức năng của JavaScript classes. Angular cung cấp sẵn nhiều decorators gắn các metadata vào các classes để dễ dàng biết được ý nghĩa và cách thức hoạt động. *NgModule* là một decorator function (single metadata object) có các thuộc tính mô tả các module. Các thuộc tính quan trọng là: - *declarations* - Khai báo các View classes thuộc về module. Angular có 3 loại view classes: components, directives, pipes. - *exports* - Một tập con của *declarations* khả dụng trong component templates của các module khác. - *imports* - Các module khác được exported các classes cần thiết bởi component templates được *declarations* bên trong module này. - *providers* - Tạo ra các global collection of services có thể truy cập ở mọi ngõ ngách của ứng dụng. - *bootstrap* - Main application view, hay còn được gọi là *root component*, chứa tất cả các app views. Duy nhất *root module* có thuộc tính `bootstrap`. Dưới đây là một * root module* đơn giản: **app/app.module.ts** ``` 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 { } ``` > Trong trường hợp này, thuộc tính `export` của AppComponent chỉ đơn giản mô tả cách export; nó thì không cần thiết trong thực tế. Một *root module* không có lý do gì để `export` bất cứ điều gì, đơn giản là các component khác ko cần thiết phải import *root module*. Khởi chạy ứng dụng bằng cách `bootstrapping` *root module*. Trong quá trình development, chúng ta sẽ bootstrap `AppModule` trong file **main.ts**. **app/main.ts** ``` import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'; import { AppModule } from './app.module'; platformBrowserDynamic().bootstrapModule(AppModule); ``` ### Angular modules vs. JavaScript modules **Angular Module**: Class decorator với `@NgModule` — đây là 1 một tính tăng cơ bản Angular. **Javascript Module**: JavaScript cũng có một hệ thống module để quản lý các JavaScript objects. Nó hoàn toàn khác biệt và không liên quan tới Angular module system. > Trong JavaScript mỗi file là một module và tất cả các đối tượng được định nghĩa trong file đều thuộc về module đó. Các module khai báo đối tượng public bằng cách sử dụng từ khóa `export`. Các module JavaScript khác sử dụng từ khóa `import` để truy cập vào đối tượng public từ các module khác. ``` import { NgModule } from '@angular/core'; import { AppComponent } from './app.component'; ``` ``` export class AppModule { } ``` Tìm hiểu thêm về [Javascript Module](http://exploringjs.com/es6/ch_modules.html). ### Angular libraries ![](https://viblo.asia/uploads/998f1c91-dc05-4e19-9d1c-2195c10886ca.png) Angular được ví như 1 con tàu chuyên chở các JavaScript modules. Cũng có thể ví là các library modules. Mỗi Angular library name đều bắt đầu với tiền tố `@angular`. Bạn cài đặt chúng với `npm package manager` và import các phần lại bằng câu lệnh JavaScript `import`. Lấy ví dụ, import Angular's `Component` decorator từ **@angular/core** library như sau: ``` import { Component } from '@angular/core'; ``` Bạn cũng có thể import *Angular modules* từ *Angular libraries* sử dụng câu lệnh JavaScript `import`: ``` import { BrowserModule } from '@angular/platform-browser'; ``` Trong ví dụ về *root module* bên trên, application module cần *BrowserModule*. Để truy cập tới *BrowserModule*, ta thêm vào `@NgModule` metadata imports: ``` imports: [ BrowserModule ], ``` Theo cách này, ta đang sử dụng cả Angular and JavaScript module systems cùng nhau. ## Components ![](https://viblo.asia/uploads/3735f092-68b6-4cb4-ac1e-0c28e5f6d85c.png) Một component điều khiển từng chức năng trên màn hình view. Ví dụ, view sau được điều khiển bởi các components: - The app root với navigation links. - The list of heroes. - The hero editor. Chúng ta cần định nghĩa component's application logic—những gì sẽ support trên view—bên trong một class. Class sẽ tương tác với view thông qua API của các properties và methods. Ví dụ, **HeroListComponent** có một heroes property trả về 1 mảng các heroes lấy từ service. **HeroListComponent** cũng có sẵn phương thức selectHero() để get các thuộc tính của selectedHero khi người dùng chọn user từ danh sách. **app/hero-list.component.ts (class)** ``` 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; } } ``` Angular creates, updates, và destroys components cùng với user di chuyển xuyên suốt trong application. App có thể lấy các action ở từng thời điểm tron lifecycle thông qua lifecycle hooks, như `ngOnInit()` được khai báo bên trên. ## Templates ![](https://viblo.asia/uploads/c4425a98-9a0e-4635-a25f-3dd5103b8b23.png) Chúng ta định nghĩa component's view với template của nó. Một template là mã code HTML giúp Angular render component. Một template có thể có đôi chút khác biệt với HTML thông thường, đây là 1 ví dụ cho template của `HeroListComponent`: **app/hero-list.component.html** ``` <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> ``` Mặc dù template này sử dụng các thành phần cơ bản HTML như `<h2>` và `<p>`, nhưng cũng có ddôi chút khác biệt như `*ngFor`,` {{hero.name}}`, `(click)`, `[hero]`, và `<hero-detail>` sử dụng *Angular's template syntax*. Trên dòng cuối cùng của template, `<hero-detail>` tag là custom element thể hiện cho new component, `HeroDetailComponent`. `HeroDetailComponent` là một component khác với `HeroListComponent` chúng ta đang xem. `HeroDetailComponent` mô tả chi tiết từng hero, mà người dùng đã **select** từ `HeroListComponent`. `HeroDetailComponent` là con(*child*) của `HeroListComponent`. ![](https://viblo.asia/uploads/d0f19d36-3916-4721-9e7f-1b872b74f3c4.png) > Element `<hero-detail>` có thể được code thoải mái giữa native HTML elements ## Metadata ![](https://viblo.asia/uploads/9bdc0dbb-5bfc-43ec-a307-26fd72eda43e.png) **Metadata** giúp Angular biết cách xử lý các class. Quay trở lại code `HeroListComponent` bên trên, ta thấy rằng đó chỉ đơn giản là 1 class, ko có dấu ấn gì của 1 framework cả, no "Angular" omg Thực tế, `HeroListComponent` thực chất chỉ là 1 class. Nó không phải là 1 component cho tới khi ta khai báo nó với Angular. Để khai báo với Angular rằng `HeroListComponent` là 1 component, ta sẽ gắn thẻ **metadata** vào class này. Trong `TypeScript`, việc gắn thẻ **metadata** sử dụng `decorator`. Dưới đây là `metadata` cho `HeroListComponent`: **app/hero-list.component.ts (metadata)** ``` @Component({ moduleId: module.id, selector: 'hero-list', templateUrl: 'hero-list.component.html', providers: [ HeroService ] }) export class HeroListComponent implements OnInit { /* . . . */ } ``` *Decorator* ở đây chính là `@Component`, định nghĩa class ngay bên dưới như một *component class*. `@Component` decorator khởi tạo một object với các thông tin mà *Angular* cần thể tạo và biểu diễn một component & view. Dưới đây là một số option cấu hình cho `@Component`: - `moduleId`: Tập hợp các source dựa trên address (module.id) với module-relative URLs như templateUrl. - `selector`: CSS selector ra lệnh cho Angular create và insert một thể hiện component khi nó tìm thấy `<hero-list>` tag trong parent HTML. Ví dụ, nếu một app's HTML có chứa `<hero-list></hero-list>`, thì Angular sẽ inserts một instance của HeroListComponent view giữa các tags. - `templateUrl`: module-relative address của component's HTML template. - `providers`: Mảng các dependency injection providers cho services cần thiết để component hoạt động. Đây là 1 cách khai báo với Angular rằng, component's constructo yêu cầu 1 HeroService để nó có thể thu được danh sách các heroes phục vụ việc hiển thị lên màn hình. ![](https://viblo.asia/uploads/8e49084e-55a7-4f71-98e6-d3a12c5729ff.png) **Metadata** trong @`Component` giúp Angular biết cách lấy những thành phần chính tạo nên component. **Template**, **Metadata**, và **Component** được sử dụng cùng với nhau với mục đích tạo nên **View**. Ngoài `@Component`, chúng ta còn có `@Injectable`, `@Input`, và `@Output` là những *decorators* rất hay được sử dụng. ## Tham khảo https://angular.io/docs/ts/latest/guide/architecture.html