Angular 2 architecture overview (P2)
Bài đăng này đã không được cập nhật trong 3 năm
Bài viết này xin được kết thúc series mô tả kiến trúc một ứng dụng Angular 2. Để xem phần 1, các bạn có thể vào link này.
Data binding
Hãy tưởng tượng rằng, bạn đang phải code 1 ứng dụng web mà việc tương tác, thay đổi giá trị trên DOM diễn ra liên tục. Sẽ thực sự là 1 cơn ác mộng nếu bạn phải tự tay thực hiện toàn bộ các thao tác update, create value trên các HTML DOM khi có hành động của người dùng mà không sử dụng 1 framework nào khác ngoài mấy thư viện kiểu jQuery :-s
Với Angular, chúng ta có 1 thuật ngữ là data binding, một cơ chế phối hợp nhịp nhàng các thành phần của template với component. Việc này rất đơn giản, chúng ta chỉ cần thêm binding markup vào HTML là Angular tự hiểu và connect chúng tới Component.
Như diagram bên dưới, có 4 kiểu data binding syntax. Mỗi kiểu đều có chiều dữ liệu từ DOM tới COMPONENT, từ COMPONENT tới DOM, và có cả kiểu có 2 chiều dữ liệu.
Ở ví dụ HeroListComponent
, template có 3 kiểu:
app/hero-list.component.html (binding)
<li>{{hero.name}}</li>
<hero-detail [hero]="selectedHero"></hero-detail>
<li (click)="selectHero(hero)"></li>
- Markup
{{hero.name}}
có chức năng hiển thị giá trị của component's hero.name property với thẻ <li>. - Markup
[hero]
property binding truyền giá trị củaselectedHero
từ parentHeroListComponent
tới cáchero
property củaHeroDetailComponent
. (click)
event binding gọi phương thứcselectHero
khi user click vào hero's name.
Kiểu quan trọng thứ 4, được gọi là Two-way data binding. Chỉ với 1 markup notation đơn giản sử dụng ngModel
directive, ta có thể kết hợp việc binding event và property.
app/hero-detail.component.html (ngModel)
<input [(ngModel)]="hero.name">
Angular xử lý toàn bộ data bindings trên mỗi JavaScript event, xuất phát từ gốc của cây application component duyệt tới toàn bộ components lá.
Data binding đóng một vai trò quan trọng trong việc giao tiếp giữa template và component.
Data binding còn là công cụ giao tiếp giữa parent và child components.
Directives
Angular templates là dynamic. Khi ứng dụng Angular thực hiện render template, chúng transforms DOM theo các lệnh nhận được từ directives.
Một directive là một class bắt đầu với @Directive
decorator. Có thể coi một component là một directive-với-một-template; @Component
decorator thực chất là một @Directive
decorator kế thừa từ template-oriented features.
While a component is technically a directive, components are so distinctive and central to Angular applications that this architectural overview separates components from directives.
Câu trích dẫn trên cho chúng ta lý do tại sao chúng ta lại tách Component ra khỏi Directive trong kiến trúc Angular.
Có 2 loại directives: structural và attribute directives.
Chúng xuất hiện giữa element tag như 1 thuộc tính (attributes).
Structural directives tùy chỉnh layout bằng cách thêm mới, xóa bỏ và thay thế elements trong DOM.
Ví dụ dưới đây sử dụng 2 loại built-in structural directives:
app/hero-list.component.html (structural)
<li *ngFor="let hero of heroes"></li>
<hero-detail *ngIf="selectedHero"></hero-detail>
*ngFor
giúp liệt kê cáchero
từ mảngheroes
trong mỗi thẻ<li>
*ngIf
includeHeroDetail
component nếu có mộthero
được select.
Attribute directives thay thế appearance hoặc behavior của các element. Bên trong templates, chúng chẳng có gì khác các HTML attributes thông thường, ngoại trừ tên )
ngModel directive, implements two-way data binding, là một ví dụ của attribute directive. ngModel thay đổi behavior của element (Ví dụ tiêu biểu là các <input>)bằng cách thiết lập value property hiển thị ra cũng như thay đổi chúng theo events.
app/hero-detail.component.html (ngModel)
<input [(ngModel)]="hero.name">
Ngoài ra, chúng ta còn nhiều directives có thể chỉnh sửa cấu trúc layout (ngSwitch
) hoặc thay đổi DOM elements và components (ngStyle
và ngClass
).
Dĩ nhiên, bạn cũng có thể tự viết một directives. Components như HeroListComponent
chính là một loại custom directive.
Services
Service có thể là bấy kỳ giá trị, hàm, class,... tính năng nào cần thiết cho ứng dụng của bạn hoạt động.
Hầu hết mọi thứ đề có thể trở thành service (lol). Một service điển hình là một class
được thu hẹp lại trong một mục đích rõ ràng.
Ví dụ:
logging service
data service
message bus
tax calculator
application configuration
Không có một chút đặc trưng của Angular về services. Angular cũng không có bất kỳ định nghĩa nào về service.
No service base class, and no place to register a service.
Tuy nhiên services lại là nền tảng của bất kỳ ứng dụng Angular nào. Components cũng là một services.
Dưới đây là một service class
có nhiệm vụ logs lên browser console:
app/logger.service.ts (class)
export class Logger {
log(msg: any) { console.log(msg); }
error(msg: any) { console.error(msg); }
warn(msg: any) { console.warn(msg); }
}
Còn đây là HeroService
có nhiệm vụ lấy về mảng các heroes
và trả về chúng cùng với resolved Promise
. HeroService
phụ thuộc vào Logger
service và BackendService
khác nắm giữ việc communication với server.
app/hero.service.ts (class)
export class HeroService {
private heroes: Hero[] = [];
constructor(
private backend: BackendService,
private logger: Logger) { }
getHeroes() {
this.backend.getAll(Hero).then( (heroes: Hero[]) => {
this.logger.log(`Fetched ${heroes.length} heroes.`);
this.heroes.push(...heroes); // fill cache
});
return this.heroes;
}
}
Services are everywhere.
Component classes như một miếng thịt nạc không có mỡ =)) . Không có bất kì đoạn code nào fetch data từ server, validate user input, hoặc log lên console. Tất các các tasks đó được giao cho services.
Công việc của một component lúc này không hơn việc cho phép người sử dụng trải nghiệm dịch vụ. Đứng giữa view (rendered bởi template) và tầng application logic.
Khi code Angular, không ai ép bạn làm theo parttern này, cũng không quy định nguyên tắc gì về service, tuy nhiên chắc hẳn mọi người sẽ không phàn nàn nếu bạn hoàn thành 1 component với hơn 3000 lines code =))
Angular giúp ta thực hiện các nguyên tác này dễ dàng hơn bằng cách support Dependency injection.
Dependency injection
Dependency injection là một cách cung cấp các new instance của một class cùng với các dependencies phụ thuộc mà nó cần. Hầu hết các dependencies là services. Angular sử dụng dependency injection để chuẩn bị một new component cùng với các services mà nó cần.
@
Việc khai báo các services cần thiết cho một component cũng với kiểu dữ liệu của nó được thực hiện trong chính constructor parameters
. Ví dụ về constructor
của HeroListComponent
và HeroService
ở đây chính là một dependency :
app/hero-list.component.ts (constructor)
constructor(private service: HeroService) { }
Khi Angular khởi tạo một component, nó sẽ hỏi injector về các services mà component cần.
Một injector lưu giữ container
các service instances
đã được tạo trước đó. Nếu có một yêu cầu về service instance không có trong container, injector sẽ tạo và thêm mới nó rồi add nó vào container trước khi trả lại service cho Angular. Khi tất cả các request services được giải quyết (resolved) và được hoàn thành (returned), Angular có thể gọi các component's constructor cùng với các tham số là các services. Đó chính là dependency injection.
Quá trình injection của HeroService
có thể trông giống sơ đồ sau:
Nếu injector không có HeroService
, Vậy làm thể nào để Angular biết nơi cần lấy và tạo ra nó
Như đã mô tả trước đó, bạn cần phải đăng ký (registered) HeroService
với provider của HeroComponent
cùng injector. Một provider có thể create và return một service.
Bạn có thể register providers trong modules hoặc trong components.
Thông thường, chúng ta add providers vào root module, ez, ta có instance của service tồn tại ở mọi nơi (ngon).
app/app.module.ts (module providers)
providers: [
BackendService,
HeroService,
Logger
],
Một cách khác, ta có thể register ở level component trong providers property của @Component
metadata:
app/hero-list.component.ts (component providers)
@Component({
moduleId: module.id,
selector: 'hero-list',
templateUrl: 'hero-list.component.html',
providers: [ HeroService ]
})
Registering ở level component nghĩa là bạn có thể lấy ra các new instance của service với mỗi một new instance của component đó.
Các điểm cần nhớ về dependency injection:
- Dependency injection được móc vào Angular framework và có thể sử dụng ở mọi nơi.
- Injector có 2 cơ chế chính.
- Injector duy trì một container các service instances mà chúng đã tạo ra.
- Injector có thể tạo mới service instance từ provider.
- Provider là nơi khai báo cách tạo ra service.
- Register providers với injectors.
THE END
Tham khảo
All rights reserved