Bài viết này tôi sẽ giải thích rõ từng thành phần cấu tạo và cách code Angular 4. Chúng ta sẽ đi lần lượt từ dễ đến khó về các khái niệm trong Angular 4, giúp bạn có cái nhìn tổng quan về cách code chuẩn nhất một hệ thống sử dụng Angular.
Phiên bản mới nhất của Angular bây giờ là bản 6, tuy nhiên, phiên bản chính thức được sử dụng rất tốt bởi nhiều doanh nghiệp Enterprise, công ty lớn (ứng dụng nhiều người dùng, ứng dụng dành cho doanh nghiệp) và được Google hỗ trợ trọn đời (code sau 5 10 năm nữa cũng k cần chỉnh sửa gì dù công nghệ web có thay đổi ra sao đi nữa). Đó chính là phiên bản Angular 4. Do đó chúng ta hãy lấy bản này làm chuẩn. Còn bản 5, bản 6 thật ra đang là phiên bản thử nghiệm cho cộng đồng để tăng perfomance, thêm nhiều cách code hay ho hơn, nhưng cũng tiềm ẩn nhiều bugs hơn nếu xử lý code không tốt.
Nếu bạn chưa biết gì hoặc mới học code Angular, bạn vẫn có thể sử dụng bài viết này để tham khảo và học hỏi cách code. Hãy upvote, share bạn bè, và clip (bookmark) bài này nếu bạn thấy hữu ích nhé!
Components là một khối code trong app Angular. Nó là sự kết hợp của bộ template html (bộ khung html) và nhúng kèm code TypeScript (hoặc Javascript). Các components là độc lập với nhau và độc lập với hệ thống. Nó có thể được cài vào hoặc tháo ra khỏi hệ thống dễ dàng. Một component có thể hiểu như một control trên màn hình hiển thị, gồm giao diện html và code logic xử lý sự kiện đi kèm control đó. Một component cũng có thể to lớn như là cả 1 màn hình chứa nhiều control hoặc một nhóm nhiều màn hình. Tức là là một component cũng có thể chứa và gọi được nhiều component khác nối vào. Như vậy Angular 4 rất linh hoạt trong việc chia nhỏ code ra các component.
Trong Angular 4 chúng ta khai báo một Component với cấu trúc như sau:
import { Component } from '@angular/core'; @Component({ selector: 'hello-ng-world', template: `<h1>Hello Angular world</h1>` }) export class HelloWorld { }
Như chúng ta thấy từ khóa @Component sẽ giúp định nghĩa ra một bộ khung html cho nó. Và bên dưới là một class HelloWorld dùng để viết code logic. Trong định nghĩa bộ khung html, chúng ta có một số thuộc tính cần chú ý sau đây:
-
selector : Là tên được đặt để gọi một component trong code html. Ở ví dụ vừa rồi, từ khóa hello-ng-world được đặt tên cho component này. Khi cần gọi component này ra ở màn hình html cha, ta sẽ gọi bằng html tag <hello-ng-world></hello-ng-world>. Gọi như vậy thì component con sẽ được render ra component cha.
-
template : Là tự định nghĩa khung html cho component dạng string ở trong file này luôn. Ví dụ ở trên chỉ định nghĩa một thẻ html h1 đơn giản. Cách này chỉ dùng cho component đơn giản.
-
templateUrl : Là đường dẫn url tới file html bên ngoài để load file đó vào làm khung html cho component này. Đây là cách code hay được dùng vì cho phép tách riêng khung html ra khỏi code logic, người làm design sẽ sửa file html riêng, độc lập với người làm code.
-
styles : Là viết style css luôn vào file component này. Cách này chỉ dùng cho component đơn giản.
-
styleUrls : Là đường dẫn url đến file style css độc lập cho component này. Cách này khuyên dùng vì file css nên để dành riêng cho người designer đụng vào.
Angular có cách code Binding (kết nối giữa html và data) dữ liệu theo kiểu 2 chiều, nghĩa là html input thay đổi thì biến javascript sẽ ngay lập tức nhận được giá trị trả về và ngược lại, giá trị trong js thay đổi thì ngay lập tức màn hình html thay đổi theo. Chúng ta không cần đi nhặt nhạnh từng giá trị của các ô input như thời dùng jQuery nữa. Để bind một chuỗi ra ngoài màn hình html thì rất đơn giản sử dụng 2 dấu ngoặc nhọn {{TenBien}} Ví dụ chúng ta có một Component đơn giản như sau:
import { Component } from '@angular/core'; @Component({ selector: 'hello-ng-world', template: `<h1>Hello {{title}} world</h1>` //Hiển thị biến javascript ra html. Chú ý đoạn này sử dụng dấu ` thay vì dấu ', ví lúc này chúng ta nhúng cả code Angular vào string. }) export class HelloWorld { title = 'Angular 4'; //Đặt một biến giá trị javascript; }
Đó là hiển thị chuỗi string. Nhưng nếu muốn hiển thị giá trị đã có ra một ô input text, thì phải viết 2 dấu ngoặc vuông [] như sau:
<input type="text" [value]="title">
Chú ý một chút, trong ví dụ này thì value là một từ khóa html chứ không phải tên biến, title mới là tên biến dùng để truyền vào cho [value] nhưng chúng ta không đóng khung title mà đóng khung value. Điều này cho phép chúng ta linh hoạt để gán giá trị cho bất cứ thuộc tính html nào cũng được.
<input type="text" [placeholder]="title" >
Câu hỏi nhỏ: Liệu có thể viết lẫn lộn <input type="text" [placeholder]="{{title}}" > được hay không?
Để gắn một sự kiện của một control html với một hàm javascript, trong Angular 4 chúng ta viết như sau:
<button (click)="updateTime()">Update Time</button>
Khi chúng ta viết (click) tức là muốn bind sự kiện click chuột của người dùng ở control này và gọi hàm updateTime() tương ứng trong code của Component này ra xử lý.
3.1 Xử lý thao tác của người dùng
Sự kiện thì có nhiều loại. Nếu sự kiện click thì đơn giản là khi nhấn xong chúng ta mới xử lý hành động. Nhưng có một số loại sự kiện cần xử lý data ngay trong lúc người dùng đang thao tác. Ví dụ người dùng gõ phím trên input text, người dùng chọn một option trong dropdownList, giá trị đang được chọn phải phản ánh ngay sang một control khác chẳng hạn.
Lúc này chúng ta cần dùng đến biến events. Đoạn code ví dụ sau đây, ngay khi người dùng gõ phím vào textbox, giá trị của textbox sẽ được lưu lại vào một biến label:
<input type="text" (change)="updateValue($event)">
updateValue(event: Event){ // event.target.value sẽ nhận được giá trị truyền về từ textbox. // Sau đó vứt giá trị đó sang cho biến label. this.label = event.target.value; }
3.2 Xử lý thao tác của người dùng (ngon hơn cách 3.1)
Cách viết code ở ví dụ 3.1 hoàn toàn chạy ngon và không ai bắt bẻ gì. Nhưng vấn đề là biến events nó chứa rất nhiều function/component con bên trong (nó chứa toàn bộ các hàm xử lý hàng trăm loại event khác nhau của một component, một control). Do đó sẽ là không khôn ngoan khi gửi và nhận cả một cục to như vậy đưa sang javascript trong khi cái chúng ta cần chỉ là giá trị đang được nhập của textbox.
Hãy viết lại đoạn code vừa rồi như sau:
<input type="text" #label1 (change)="updateValue(label1.value)">
updateValue(value: any){ // chỉ cần nhặt giá trị text được truyền về nhét vào biến label là xong this.label = value; }
Như chúng ta thấy, hàm updateValue() lúc này nhận truyền vào là một biến #label1 (biến này được gọi mỹ miều là template reference variable) và label1.value sẽ chứa giá trị được bind với textbox hiện tại.
Cách viết này rất được khuyên dùng. Các bạn lười quá thì viết kiểu 3.1 cũng được nhưng nếu biết chính xác cái sự kiện mình cần là gì thì nên viết theo 3.2.
3.3 Xử lý sự kiện binding 2 chiều
Ban nãy, tôi đã bảo các bạn là viết như thế này là binding 2 chiều:
<input type="text" [value]="title">
Nhưng tôi lừa các bạn đấy, viết như thế này là chỉ bind một chiều, nghĩa là chỉ show được giá trị ra thôi chứ không lấy được giá trị từ html tự động bỏ vào biến title. Ví dụ viết như sau thì giá trị của h1 sẽ không thay đổi gì khi ta gõ vào ô input
<h1> {{title}} </h1> <input type="text" [value]="title">
Từ phiên bản Angular 2 trở lên thì anh Google đã quyết định là không nên cái gì cũng bind 2 chiều.
Đó là vì bind 2 chiều sẽ làm khổ html. Nó sẽ phải render liên tục khi người dùng gõ vào ô input.
Nhưng trong trường hợp chúng tôi cần phải bind 2 chiều thì làm thế nào?
Rất đơn giản, lúc này chúng ta sẽ viết theo kiểu kẹp chuối.
Viết theo cú pháp bên dưới đây sẽ giúp bạn bind được 2 chiều, khi gõ vào ô input thì giá trị của h1 sẽ thay đổi tương ứng:
<h1> {{title}} </h1> <input type="text" [(ngModel)]="title" name="title">
Từ khóa ngModel lúc này không phải là thuộc tính html nữa mà nó là từ khóa của Angular 4. Khi viết [(ngModel)] chúng ta sẽ gắn chặt giá trị của input html với biến title. Dẫn đến người dùng gõ vào ô này thì biến js title thay đổi theo => ô h1 hiện giá trị tương ứng của title. Cách viết [()] được gọi là Banana in a box hay nôm na là kẹp chuối.
Từ khóa ngModel như ta thấy bên trên, có thể dùng để bind 2 chiều. Hiện đại hơn, biến này còn chứa cả các class CSS được gắn với biến model thể hiện tính hợp lệ của data đang được nhập vào form control.
<input type="text" [(ngModel)]="vm.fname" name="firstName" required /> {{fname.className}} <br /> <input type="text" [(ngModel)]="vm.lname" name="lastName" /> {{lname.className}}
Biến vm trong component chứa 2 thuộc tính là fname và lname. Chúng ta bind nó ra 2 input html để nhập giá trị cho nó. Tuy nhiên cần validate giá trị nhập vào. Nên ta kiểm tra bằng cách gọi {{fname.className}} để xem class của input textbox hiện tại đang là cái gì.
Chú ý, khi sử dụng ngModel trong một form html. Bắt buộc phải có attribute name. Nếu không có thì sẽ bị báo lỗi như sau:
If ngModel is used within a form tag, either the name attribute must be set or the form control must be defined as 'standalone' in ngModelOptions.
Chúng ta sẽ có các class tự động sinh ra ở input type="text" như sau:
- ng-untouched - Class CSS này nghĩa là page đã load xong và input chưa được đụng đũa vào.
- ng-touched - Người dùng đã sờ vào control, ví dụ dí chuột hoặc nhấn chuột vào nó.
- ng-pristine - Class này nghĩa là input có giá trị được bind sẵn vào nhưng chưa bị sửa đổi
- ng-dirty - Giá trị bên trong đã bị sửa đổi, người dùng đã chọc ngoáy vào
- ng-valid - Người dùng nhập giá trị hợp lệ
- ng-invalid - Người dùng nhập giá trị dữ liệu không hợp lệ. Ví dụ bỏ trống một input required
Các class sẽ tự động sinh ra và gắn vào input mỗi khi có sự thay đổi dữ liệu trên form. Và nhiệm vụ của bạn là định nghĩa css style tương ứng với các thay đổi đó. Ví dụ:
.ng-invalid{ background: orange; }
Sau khi áp dụng style thì sẽ như sau:
Module là một khái niệm rộng nhất của Angular. Một module có thể bao gồm chứa các components, directives, pipes, v.v.
Module có thể được biên dịch (compile) dưới dạng ahead-of-time (AoT). Nghĩa là biên dịch ra mã thực thi để hiện ra luôn trên trình duyệt không cần vẽ vời gì từ đầu. Hãy tưởng tượng component có html và js viết riêng, khi load trang thì 2 thứ này mới nhào nặn chung để hiển thị html+data lên màn hình. AoT là thứ html+data đã nhào sẵn.
Module cũng có thể gọi module con và bắt tay được với các module khác.
Ví dụ về module chúng ta có thể bắt gặp ngay ở trong app.module.ts
import { NgModule } from '@angular/core'; import { BrowserModule } from '@angular/platform-browser'; import { MyAppComponent } from './app.component'; @NgModule({ imports: [ BrowserModule ], declarations: [ MyAppComponent ], bootstrap: [ MyAppComponent ] }) export class MyAppModule { }
Như vậy thì bản thân một app Angular chính là một module khổng lồ, trong đó cài cắm nhiều module con.
Các thuộc tính của module được định nghĩa như sau:
- imports: Định nghĩa sự phụ thuộc (Dependency) của module này, module phụ thuộc sẽ được load trước rồi module này mới load.
- declarations: Định nghĩa tất cả các component sẽ được dùng trong module này. Nếu chưa định nghĩa thì các component trong module sẽ không thể gọi nhau vì không tìm thấy nhao.
- bootstrap: Mỗi ứng dụng Angular đều cần một module gốc, module này sẽ có một component gốc chứa layout gốc sẽ được render ra ở file index.html.
Một service là một đoạn code trong ứng dụng Angular mà phục vụ cho tác vụ gì đó, xử lý code logic gì đó. Ví dụ handle (xử lý) data gửi nhận từ/đến một API, hoặc cung cấp hàm authenticate...
Tất nhiên là service thì chỉ có code không hề có giao diện.
Để tạo ra một service thì chúng ta cần import và mô tả một class với từ khóa @injectable lấy từ @angular/core module.
Ta hãy lấy một ví dụ:
import { Injectable } from '@angular/core'; @Injectable() export class TimeService { constructor() { } getTime(){ return `${new Date().getHours()} : ${new Date().getMinutes()} : ${new Date().getSeconds()}`; } }
Sau khi định nghĩa class TimeService là @Injectable() thì chúng ta sẽ gọi được service này ở nhiều chỗ component khác trong ứng dụng.
6.1 Dependency Injection (DI)
Khi một class muốn được gọi (được tiêm vào, inject vào) một component cần gọi hàm bên trong nó, chúng ta cần dùng đến Dependency Injection. Và rất đơn giản chỉ cần gọi ở hàm khởi tạo (constructor) của component là sẽ tiêm được service vào để dùng nó bên trong component đó.
import { Component } from '@angular/core'; import { TimeService } from './time.service'; @Component({ selector: 'app-root', providers: [TimeService], template: `<div>Date: {{timeValue}}</div>, }) export class SampleComponent { timeValue: string; constructor(private time: TimeService){ this.timeValue = this.time.getTime(); //Gọi biến time từ } }
6.2 Viết service dưới dạng dùng chung
Nếu chúng ta dùng service ở nhiều nơi cùng lúc và không muốn khai báo nhiều lần, component nào cũng phải tiêm nó vào. Thì lúc này có thể khai báo service ở phần providers của module hoặc component luôn.
@NgModule({ declarations: [ AppComponent, SampleComponent ], imports: [ BrowserModule ], providers: [TimeService], bootstrap: [AppComponent] }) export class AppModule { }
Directives có thể hiểu như là các đoạn mã typescript (hoặc javascript) kèm theo cả html và khi gọi thì gọi như là html luôn, ví dụ:
<div *ngIf="title"> Time: {{title}} </div>
Từ Angular 2 trở đi, directives được chia làm các loại sau đây:
- Components: Không có nghi ngờ gì khi gọi component là directive cũng được, vì rõ ràng là component cho phép định nghĩa selector và gọi ra như một thẻ html tag (<component-name></component-name>)
- Structural directives: Là directive cấu trúc, dùng để vẽ html, hiển thị data lên giao diện html. Ví dụ ngFor, ngIf
- Attribute directives: Thêm các thuộc tính động cho element html, ví dụ ngStyle
7.1 Ng-if… else
Đây là một Structural directives, có tác dụng kiểm tra điều kiện ngay ở html. Ví dụ:
<div *ngIf="title; else noTitle"> Time: {{title}} </div> <ng-template #noTitle> Click on the button to see time. </ng-template>
Code ở trên, khi biến title có giá trị, thì chuỗi Time: [value] được show ra. Và cục #noTitle template bị ẩn đi, ngược lại thì điều kiện else được chạy và #noTitle được hiện ra.
Như ta thấy dùng cái directive ngIf else này rất tiện lợi khi có thể ẩn hiện html dễ dàng.
7.2 Ng-Template
Đây cũng là một Structural directives. Nó giúp gom cục html cần ẩn hiện.
<div *ngIf="isTrue; then tmplWhenTrue else tmplWhenFalse"></div> <ng-template #tmplWhenTrue >I show-up when isTrue is true. </ng-template> <ng-template #tmplWhenFalse > I show-up when isTrue is false </ng-template>
Chú ý là đoạn code vừa rồi dùng ngIf..then..else. Cách viết này đầy đủ hơn của 7.1
7.3 Ng-Container
Tương tự như Ng-Template dùng để gom html. Nhưng điểm mạnh của Ng-Container là thẻ directive này không render ra tag <ng-container> html như là <ng-template> mà tag sẽ được ẩn đi, giúp cho layout css không bị vỡ nếu bạn gom html (Không sợ bị nhảy từ div cha sang div con, cấu trúc html k hề thay đổi khi gom vào tag <ng-container></ng-container>)
Hãy xem ví dụ sau đây:
Welcome <div *ngIf="title">to <i>the</i> {{title}} world.</div>
Sẽ được render ra như sau:
Khi soi html chúng ta sẽ thấy:
Tự dưng dòng div có ngIf nó lại chèn một cái thuộc tính _ngcontent-c0, dẫn đến dòng đó bị xuống dòng, làm sai layout design.
Bây giờ hãy viết lại như sau:
Welcome <ng-container *ngIf="title">to <i>the</i> {{title}} world.</ng-container>
Kết quả sẽ nuột nà ngay:
Đó là vì html đã được dọn gọn gàng:
7.4 NgSwitch and NgSwitchCase
Chúng ta hoàn toàn có thể sử dụng câu lệnh điều kiện switch case trong Angular y như switch case trong Javascript vậy.
Cách viết như sau:
<div [ngSwitch]="isMetric"> <div *ngSwitchCase="true">Degree Celsius</div> <div *ngSwitchCase="false">Fahrenheit</div> </div>
Trong trường hợp muốn dùng switch case default (nếu toàn bộ case k thỏa màn thì vào default) thì chúng ta viết như sau:
<div [ngSwitch]="isMetric"> <div *ngSwitchCase="true">Degree Celsius</div> <div *ngSwitchDefault>Fahrenheit</div> </div>
7.5 Hàm @Input()
Một directive hoàn toàn có thể nhận giá trị truyền vào để hiển thị hoặc tính toán.
Hãy xem một ví dụ sau đây. Giả sử chúng ta khai báo một component Login như bình thường. Nhưng có thêm một biến showRegister định nghĩa với từ khóa @Input()
Import Input() import { Component, OnInit, Input } from '@angular/core'; ///...các đoạn code khác @Input() showRegister: boolean;
Chúng ta định nghĩa template html của component như sau:
<div> <input type="text" placeholder="User Id" /> <input type="password" placeholder="Password" /> <span *ngIf="showRegister"><button>Register</button></span> <button>Go</button> </div>
Khi gọi render ra component login thì truyền cái biến Input cho nó như sau:
<login showRegister="true"></login>
Nếu bạn muốn giá trị của biến showRegister là được lấy động từ một biến khác, thì vẫn có thể viết như sau nhé:
<login [showRegister]="isRegisterVisible"></login>
Trong đó isRegisterVisible là biến true hoặc false tùy tình hình.
Thế nếu tôi muốn viếtshowRegisterlàshow-registercho đúng chuẩn html css thì sao?
vẫn được nhé, hãy viết như sau ở khai báo @Input()
@Input("should-show-register") showRegister: boolean;
<login should-show-register="true"></login>
7.6 Hàm Output
Một directive nhận giá trị về để hiển thị được. Vậy có thể nào bên trong directive trả giá trị ngược trở lại được không? Được luôn.
Lúc này chúng ta sẽ cần dùng đến EventEmitter (lấy giá trị trả về từ sự kiện người dùng thao tác). Ví dụ khi người dùng nhập xong id và password của một directive. Chúng ta muốn nhận được giá trị đã nhập ở một directive gọi directive login chứ không chỉ xử lý id và pass bên trong directive login.
Lúc này cần import thư viện EventEmitter trước:
import { Component, OnInit, Input, Output, EventEmitter } from '@angular/core';
Sau đó khai báo một @Output đầu ra và biến này có kiểu là EventEmitter. Chú ý lúc này là đang viết trong directive con.
@Output() onLogin: EventEmitter<{userId: string, password: string}>; constructor() { this.onLogin = new EventEmitter(); //Khởi tạo một EventEmitter mới và gắn cho biến onLogin. }
Ở html của directive con thì xử lý sự kiện click bình thường
<button (click)="loginClicked(userId.value, password.value)">Go</button>
Và ở code logic thì bắn giá trị nhận được từ input gửi sang cho thằng EventEmitter onLogin đang chờ chực sẵn.
loginClicked(userId, password){ this.onLogin.next({userId, password}); }
Giờ thì sang viết ở directive cha gọi directive <login>, chúng ta sẽ dùng EventEmitter onLogin và gán nó cho hàm loginHandlerAtParrent để directive cha toàn quyền xử lý.
<login (onLogin)="loginHandlerAtParrent($event)"></login>
Trong code directive cha, giờ thì sự kiện nhận được từ directive con sẽ chứa Object truyền sang (là id và pass nhập từ directive con)
loginHandlerAtParrent(event){ console.log(event); // Output: Object {userId: "sampleUser", password: "samplePassword"} }
Như vậy là chúng ta đã biết cách để bắn giá trị từ directive con sang directive cha rồi nhé
Để binding được 2 chiều thì rõ ràng Angular phải có cơ chế để phát hiện sự thay đổi, cho dù là từ html hay là biến js thay đổi giá trị, đều phải quét lần lượt các component thì mới biết được cái gì thay đổi.
Angular dùng cơ chế là quét từ trên xuống dưới (top-down), tức là từ component cha rồi đến component con được lần lượt kiểm tra.
Làm thể nào mà Angular quét được các sự thay đổi này?
Rất đơn giản, mỗi một component trong Angular đều có một class đi kèm với nó để so sánh. Khi có sự thay đổi value của từng biến trong component (do người dùng nghịch vào các events), lúc này giá trị ban đầu của component được lưu trước đó sẽ lấy ra so sánh. Nếu model có sự thay đổi, hiển nhiên là Angular lập tức gọi html làm việc, render ra giá trị mới trên DOM tức khắc.
Đấy là cách lưu giá trị và so sánh cho các kiểu dữ liệu giá trị như là string, bool, int...Còn nếu dữ liệu là kiểu class phức tạp mà người dùng tự định nghĩa ra. Thì lúc này cơ chế so sánh lưu giá trị sẽ dùng 1 trong 2 cách sau:
Shallow Comparison (So sánh nông): Tức là một trong số các thuộc tính của biến này (biến này là một class to) thay đổi thì cả biến đó vẫn không được nhận xét là đã thay đổi và không cập nhật giá trị. Tại sao lại so sánh khập khiễng như thế này? Cái này sẽ giúp ích trong trường hợp chúng ta dùng class dạng immutable (dùng biến như là hằng số). Rõ ràng cần phải tạo một biến mới để chứa giá trị thay đổi chứ bản thân giá trị đó k được phép thay đổi => update làm gì cho mất công.
Deep Comparison (So sánh sâu): Đi lần lượt từng thuộc tính của biến và so sánh giá trị đã bị thay đổi. Dĩ nhiên là toàn bộ giá trị của class phải được lưu lại trước đó để so sánh. Khi một trong số thuộc tính bị thay đổi, toàn bộ biến đó coi như bị thay đổi và toàn bộ html cần thay đổi theo.
Ở mục 7 chúng ta đã biết về việc một Directive có thể nhận đầu vào và cả xuất ra đầu ra. Nhưng chúng ta chỉ có thể nhập và xuất directive ở thuộc tính html của tag gọi directive. Ví dụ ngIf, ngFor, ng-template...
Nếu bây giờ chúng ta lại muốn viết vào giữa tag gọi directive thì sao? ví dụ viết vào giữa begin và end tag?
Trong Angular bản 1.X thì có một khái niệm là Transclusion (trao đổi giá trị giữa các directive). Lên Angular 4 thì vẫn hỗ trợ. Lúc này ta có thể viết như sau:
<blue>sample text</blue>
Sau khi định nghĩa ra component <blue> như bên trên, ta muốn lấy ra ruột của nó là chữ sample text và in ra chỗ khác thì sử dụng một <ng-content> để in ra như sau:
<div class="blue"> <ng-content></ng-content> </div>
Chỗ này hơi khó hiểu một chút vì không có lời gọi directive (component) <blue> gì ở html cả, mà thật ra là gọi ngầm bên dưới code ts. Component cha đã inject và gọi component blue rồi, và để lấy được ruột của blue ra thì dùng tag <ng-content> là sẽ lấy đc ra, và chèn ng-content vào chỗ nào cần chèn nữa là xong.
Rõ ràng là việc lấy được ruột html của directive cực kỳ hữu ích khi mà chúng ta có thể chứa html của html css modal bootstrap trong đó chẳng hạn và chỉ cần khai báo 1 lần dùng ở nhiều nơi.
Chúng ta biết là Observables là luồng dữ liệu được gửi nhận từ api. Observables sẽ chứa data json từ server gửi tới nhưng nó không phải là data json thuần. Nếu đưa luồng data này cho html thì nó sẽ không hiểu được để render ra màn hình. Tuy nhiên với từ khóa pipe async chúng ta có thể in ra trực tiếp như sau:
<div *ngIf="asyncData | async; else loading; let title"> Title: {{title}} </div> <ng-template #loading> </ng-template>
Lúc này biến Observables asyncData được gọi kèm từ khóa async, data bên trong nó sẽ được in ra html.
Nếu chúng ta có một mảng ở dạng Observables thì cũng hoàn toàn có thể in ra trực tiếp bằng async sử dụng ngFor
<ul *ngFor="let color of colors | async"> <li>{{color}}</li> </ul>
TypeScript 4 hỗ trợ việc check giá trị Null hoặc Undefined và không cho phép gán 2 loại giá trị này cho một biến đã được khai báo kiểu cụ thể. Ví dụ:
var firstName: string, lastName: string ; firstName = null; //sẽ báo lỗi, vì null không thể nào gắn được cho string. lastName = undefined; //sẽ báo lỗi, vì undefined không thể nào gắn được cho string.
Một biến cũng hoàn toàn có thể được khai báo là một trong 2 kiểu dữ liệu, như ví dụ dưới đây:
var firstName