TEMPLATE SYNTAX
Ứng dụng Angular2 quản lý những gì mà người dùng thấy được thông qua sự tương tác của một thực thể lớp thành phần (Component Class) và template trực diện người dùng của chính nó. Trong Angular2, Component giữ vai trò là Controller/ViewModel và Template đại diện cho khung nhìn (View). Để viết một Template cho View cần những yếu tố cơ bản của Template Syntax:
1. HTML
HTML là ngôn ngữ của Template trong Angular2. Hầu hết tất cả các cú pháp HTML là cú pháp Template hợp lệ. Ngoại trừ phần tử
<script>
không được phép vì để loại trừ khả năng bị tấn công bởi nhúng mã Javascript. Trong Angular2 có thể mở rộng vốn từ HTML trong template như các phần tử và thuộc tính (attribute) mới với các Component và các Directive.
Ngoài ra trong Angular2, chúng ta có thể tạo ra các phần tử HTML mới với các Component và cho chúng vào HTML sẵn có của chúng ta như thể chúng là các phần tử HTML bình thường.
2. Interpolation
Để hiển thị một giá trị sử dụng phép nội suy với cú pháp: {{}}
Sử dụng phép nội suy để đan xen các chuỗi đã tính toán vào văn bản giữa các thẻ thành phần HTML và bên trong các phép gán thuộc tính.
Ở giữa dấu ngoặc
{{}}
là một biểu thức mà Angular2 đầu tiên sẽ tính toán sau đó chuyển đổi thành một chuỗi và nối chuỗi đó với chuỗi chữ lân cận, thường là tên một component property.
Angular2 thay thế tên đó với chuỗi giá trị Component property tương ứng.
3. Binding syntax
Data binding là một cơ chế cho việc kết hợp những gì người dùng nhìn thấy với các giá trị dữ liệu ứng dụng. Angular2 cung cấp nhiều loại data binding và có thể nhóm tất cả các liên kết (bindings) thành 3 loại theo hướng luồng dữ liệu. Mỗi một loại có cú pháp đặc biệt riêng:
Các biểu thức phải được đặt trong dấu ngoặc kép, ngoại trừ các biểu thức nội suy. Tất cả các kiểu liên kết trừ phép nội suy đều có một target name bên trái dấu bằng, hoặc được đặt trong các dấu ngoặc
(), []
hoặc sau một tiền tố (bind-
, on-
, bindon
-).
Xét ví dụ:
Trong ví dụ trên, nếu nhìn lướt qua có thể nghĩ rằng chúng ta đang liên kêt với một attribute
disabled
của button và thiết lập nó với giá trị hiện tại của property của Component : isUnchanged
.
Trong thực tế, một khi bắt đầu liên kết dữ liệu, chúng ta không còn làm việc với các attribute HTML. Chúng ta không thiết lập các attribute HTML mà thiết lập các property của các phần tử DOM, Component và Directive.
Điều này rất quan trọng, Template liên kết với properties và events, mà không phải attributes.
Trong Angular2, vai trò duy nhất của attributes là để khởi tạo phần tử và trạng thái directive. Khi liên kết dữ liệu tức là chúng ta đang xử lý riêng với element, directive properties và events.
Binding Targets
Target của một data binding là một thứ gì đó trong DOM. Phụ thuộc vào kiểu liên kết, target có thể là một property của (element | component | directive), một event của (element | component | directive), hoặc một attribute name (rất ít).
Bảng dưới đây liệt kê các loại liên kết mà chúng ta có thể dùng:
Chú ý: Target name phải được viết bằng chữ thường.
4. Property binding
Property binding là cú pháp mà chúng ta sử dụng dấu ngoặc {} để liên kết các giá trị property của một phần tử.
Property binding phổ biến nhất là thiết lập một element property tới một giá trị Component property như khi chúng ta liên kết các property gốc của một phần tử image với property heroImageUrl
của Component.
...hoặc disable một button khi component cho biết nó là
isUnchanged
.
...hoặc thiết lập một property của một directive
...hoặc thiết lập model property của một custom component (một cách tuyệt vời cho các thành phần cha-con giao tiếp).
Property binding thường được mô tả như “one-way data binding” bởi giá trị đi theo một hướng, từ data property của một component ra một element property.
Chú ý: Ngoài ra cách viết
bind-src
cũng có thể thay thế cho [src]
5. Attribute, Class, and Style Bindings
a. Attribute Binding
Chúng ta có thể thiết lập giá trị của một attribute trực tiếp với một Attribute binding.
Đây là trường hợp ngoại lệ duy nhất mà một liên kết (binding)tạo ra và thiết lập một attribute.
Chúng ta phải sử dụng Attribute binding khi không có element property để liên kết.
Xét ví dụ:
một lỗi sẽ được thông báo:
Như thông báo lỗi cho biết, các phần tử
<td>
không có colspan
property . Nó có colspan
attribute nhưng phép nội suy và Property Binding chỉ thiết lập properties, không phải attributes. Chúng ta cần một Attribute binding để tạo ra và liên kết với các attribute như vậy.
Cú pháp Attribute binding tương tự như Property binding. Thay vì một element property trong dấu ngoặc {}
, chúng ta bắt đầy với từ khoá attr theo sau là tên attribute ([attr.attribute-name]
). Dưới đây là cách chúng ta có thể sử dụng nó để thiết lập colspan
attribute cho 1 hàng (row) trong table:
Kết quả sẽ được như hình dưới:
b. Class Binding
Chúng ta có thể thêm hoặc loại bỏ các tên class CSS từ class attribute của một phần tử với Class binding.
Cú pháp của Class Binding cũng giống Property binding. Thay vì một element property trong dấu ngoặc {}
chúng ta bắt đầu với từ khoá class theo sau là tên một CSS class: [class.class-name]
. Angular2 thêm tên class khi biểu thức template là đúng và loại bỏ tên class nếu biểu thức đó là sai.
c. Style Binding
Chúng ta có thể thiết lập các style nội tuyến với Style Binding.
Cú pháp của Style Binding giống với Property Binding. Thay vì một element property trong dấu ngoặc {}
, chúng ta sẽ bắt đầu với một từ khoá style và theo sau là tên của một CSS style property : [style.style-property]
.
Một số style của Style Binding có phần đơn vị mở rộng. Ở đây chúng ta có điều kiện thiết lập
font-style
tại các đơn vị “em
” và “%
”.
6. Event binding
Property Binding đã rất “mạnh” nhưng như thế chưa đủ vì dữ liệu chỉ theo một hướng từ data property của một Component ra một element property. Đôi khi chúng ta cần để có thể phản ứng với những điều nào đó xảy ra trong thời gian chạy ứng dụng ví dụ như chúng ta nhập text vào một textbox, chọn các mục từ một danh sách, nhấn các nút... Các hành động này có thể dẫn đến một luồng dữ liệu theo hướng ngược lại, từ một element đến Component. Cách duy nhất để biết về một hành động của người dùng là lắng nghe các sự kiện như : di chuyển chuột, nhấp chuột,... Chúng ta khai báo trong hành động người dùng thông qua Angular Event Binding. Các Event Binding sau đây lắng nghe các sự kiện (click)
của button và gọi đến phương thức onSave()
của Component:
Cú pháp của Event Binding bao gồm một target event bên trái dấu bằng và một biểu thức bên phải :
(click)=“onSave()”
. Ta cũng có thể viết on-click
thay cho (click)
.
Các element event nên có target name “tương tự” với một event property của một directive đã được biết đến trong Angular 1.x:
Nếu target name không phù hợp với một element event hoặc một output property của một directive đã biết, Angular sẽ thông báo một lỗi : “unknown directive”
$event và sự kiện xử lý biểu thức
Trong một Event binding, Angular2 thiết lập một xử lý sự kiện cho target event. Khi sự kiện được nêu ra, các việc xử lý thực hiện biểu thức template. Biểu thức template thường liên quan đến việc muốn làm một điều gì đó để đáp ứng sự kiện như lấy một giá trị từ HTML control và lưu trữ nó trong một model. Các thông tin chuyển tải liên kết về sự kiện bao gồm các giá trị dữ liệu thông qua một đối tượng sự kiện (event object) được đặt tên là $event
. Các dạng của $event
được xác định bởi target event của chính nó. Nếu target event là một element event DOM có trước thì $event
là một DOM event object với properties như target
và target.value
.
Xem xét ví dụ dưới:
Chúng ta đang liên kết giá trị textbox với property: firstName
và lắng nghe những thay đổi bằng cách liên kết với sự kiện đầu vào (input event) của textbox. Khi người dùng thực hiện các đổi, input event được xây dựng và biểu thức bên trong dấu ngoặc {}
bao gồm đối tượng $event
được thực hiện. Chúng ta phải theo đúng dạng $event.target.value
để lấy được văn bản đã thay đổi vì vậy chúng ta có thể cập nhật firstName
.
Xét ví dụ sau với HeroDetailComponent:
Component khai báo một input property có tên là “
hero
” và một output property có tên “deleted
”. Chỉ những input property của một Component có thể được cập nhật bằng cách sử dụng property binding. Ví dụ trên nói với Angular2 rằng nếu sự kiện “deleted
” phát sinh thì nó sẽ gọi đến phương thức onDeleted()
.
EventEmitter
là một sự thực thi của cả observable và observer interfaces (xem tại đây). Vì vậy chúng ta có thể sử dụng nó để khởi động các sự kiện, và Angular2 có thể sử dụng nó để lắng nghe các sự kiện.
Liên kết của chúng ta sẽ đưa đối tượng hero
cần xoá thông qua biến $hero
đến phương thức cha onDeleted()
, phương thức này biết được điều gì là cần thiết để xoá đối tượng hero
đó.
Event bubbling and propagation (sự kiện nổi bọt và sự lan truyền)
Angular2 gọi các biểu thức event-handling (quản lý sự kiện) nếu sự kiện được đưa ra bởi các phần tử hiện tại hoặc một trong các phần tử con của nó.
Nhiều sự kiện DOM (cả native và custom), các sự kiện “bubble” sẽ đi lên phía “tổ tiên” cây của các phần tử DOM theo tính chất lan truyền và ngừng lại nếu có sự ngăn cản sự lan truyền đó trên đường đi. Kết của của một biểu thức Event Binding xác định nếu sự lan truyền sự kiện tiếp tục hoặc dừng lại với phần tử hiện tại.
Kết quả của một biểu thức Event Binding xác định nếu sự lan truyền sự kiện tiếp tục hoặc dừng lại với phần tử hiện tại. Sự lan truyền dừng lại nếu biểu thức trả về một giá trị false
(chẳng hạn như void
) :
Sự lan truyền tiếp tục nếu biểu thức trả về một giá trị true
:
7. Two-way binding với NgModel
Two-way data binding rất thuận tiện trong các tình huống nhất định, đáng chú ý nhất là để xử lý đầu vào (input). Chúng ta đã biết rằng, Property binding được sử dụng để truyền dữ liệu từ cha sang con, và Event binding được sử dụng để truyền dữ liệu từ con sang cha. Vì vậy chúng ta có thể thực hiện liên kết dữ liệu hai chiều (two-way data binding).
Directive NgModel phục vụ mục đích đó như đã thấy trong ví dụ dưới đây:
Chúng ta cũng có thể viết như sau nếu thích sử dụng các tiền tố hơn:
Cách khác cũng cho kết quả tương tự như NgModel, phân tách riêng Property và Event Bindings:
Các ví dụ trên cho thấy làm thế nào để thực hiện các hành vi liên kết dữ liệu hai chiều: intput sẽ được cập nhật khi thay đổi firstName
property, và giá trị của firstName
property sẽ được cập nhật khi thay đổi input.
Một directive có thể bao bọc các giá trị property của phần tử, lắng nghe sự kiện đầu vào và đưa ra sự kiện của bản thân nó với văn bản đã thay đổi sẵn sàng để sử dụng. Directive đó là ng-model
. Chúng ta có thể viết lại ví dụ trên như sau:
Chúng ta có thể viết đơn giản hơn thế vì Angular2 cung cấp một cú pháp
[(...)]
– kết hợp các ký hiệu property binding và event binding.
Và trước khi sử dung NgModel, chúng ta phải
import
nó thông qua mảng directive FORM_DIRECTIVES`````` từ thư viện angular2:
Và include mảng đó trong danh sách các directive được sử dụng bởi template của component:
directives: [FORM_DIRECTIVES]
.
8. Built-in Directives
Ở những phiên bản trước của Angular bao gồm hơn 70 directive được xây dựng sẵn, nhưng Angular2 không cần nhiều directive như vậy. Ở phiên bản mới này chúng ta có thể viết một directive để xử lý một sự kiện click chuột đơn giản như sau:
Chúng ta vẫn được hưởng lợi từ các directive giúp đơn giản hoá các công việc phức tạp.
CORE_DIRECTIVES
là một mảng directive mà chúng ta có thể import
từ thư viện angular2 nếu chúng ta muốn sử dụng các directive có sẵn.
Sau đó include
CORE_DIRECTIVES
vào trong danh sách các directive được sử dụng bởi template của Component:
NgClass
Chúng ta thường kiểm soát các phần tử xuất hiện bằng cách thêm vào hoặc loại bỏ các class CSS. Chúng ta liên kết với NgClass để thêm/xoá một số class cùng lúc và có thể sử dụng Class Binding để thêm/xoá một single class.
Directive NgClass có thể là lựa chọn tốt khi chúng ta muốn thêm và loại bỏ nhiều class cùng một lúc.
Áp dụng NgClass bằng cách gắn vào nó một key: giá trị điều khiển đối tượng. Mỗi key của đối tượng là một tên Class và giá trị của nó là
true
nếu Class được thêm vào, là false
nếu Class cần được loại bỏ.
Xét một phương thức component:
Ngoài ra có thể thêm một Property Binding NgClass giống như sau:
NgStyle
Chúng ta có thể thiết lập các style nội tuyến (inline styles) dựa vào trạng thái của Component. Để thiết lập nhiều style nội tuyến cùng một lúc cần liên kết với NgStyle và để thiết lập một giá trị single style có thể sử dụng một Style Binding.
NgStyle directive là lựa chọn tốt khi muốn thiết lập nhiều style nội tuyến cùng lúc. Tương tự NgClass, áp dụng NgStyle bằng cách gắn vào nó một key: giá trị điều khiển đối tượng. Mỗi key của đối tượng là một tên style và giá trị của nó là bất cứ cái gì thích hợp cho style đó.
Xét ví dụ:
Với NgStyle Property Binding ta có:
NgIf
Ta có thể thêm một phần tử sub-tree (một phẩn tử và con của nó) vào DOM bằng cách gắn một directive NgIf cho một biểu thức có giá trị true
, liên kết với một biểu thức false
loại bỏ phần tử sub-tree từ DOM.
xem thêm biểu thức điều kiện với NgIf phần Displaying data.
NgSwitch
Chúng ta liên kết với NgSwitch khi muốn chèn một phần tử sub-tree (một phần tử và con của nó) vào DOM từ một tập các sub-tree khác.
Ví dụ:
Chúng ta liên kết phần tử NgSwitch cha với một biểu thức trả về một “switch value”. Trong ví dụ, giá trị là một string nhưng nó có thể là một giá trị của bất cứ kiểu gì. Phần tử Switch cha kiểm soát một tập các phần tử
<template>
con. Mỗi <template>
là một “ứng cử viên”. Một template được xác định một giá trị phù hợp (match value) hoặc được đánh dấu là template mặc định.
Tại bất kỳ thời điểm cụ thể, chỉ có một trong những template này được trả lại:
- Nếu “match value” của template bằng “switch value”, Angular bổ sung sub-tree của template vào DOM.
- Nếu không template nào phù hợp thì template mặc định sẽ được Angular bổ sung vào DOM.
Có 3 directive liên quan ở đây:
ng-switch
: liên kết vào một biểu thức trả về “switch value”ng-switch-when
: liên kết với một biểu thức trả về một “match value”ng-switch-default
: một thuộc tính đánh dấu trên template mặc định.
NgFor (xem tại Displaying Data)
9. Local Template Variables
Một biến template cục bộ là một phương tiện để di chuyển dữ liệu qua các element line. Một biến cục bộ #
có thể là một con trỏ trỏ đến một đối tượng hoặc một phần tử DOM.
Ví dụ:
Hai cách viết trên là tương đương nhau.
Trong kết quả HTML, Angular2 vượt qua vòng lặp hiện tại từ bên ngoài <template>
vào bên trong Component <little-hero>
bằng phương tiện của một biến cục bộ gọi là hero
(#hero
)
Chúng ta có thể tham chiếu một biến template cục bộ trên cùng phần tử, trên một phần tử anh em hoặc trên bất kỳ con nào của nó.
Dưới đây là 2 ví dụ về việc tạo ra và sử dụng một biến template cục bộ:
10. Input and Output Properties
Khi các Directive là các target của việc liên kết dữ liệu (data binding), chúng phải chỉ rõ input và output properties.
Các Directive có thể có nhiều property có thể liên kết. Vậy tại sao chỉ một ít trong số đó là các input hoặc output? Hoặc tất cả đều không phải? Một directive property có phải chỉ có một hướng vào (in) hoặc ra (out)?
Input/Output properties có thể là các target của một biểu thức liên kết. Một directive property trong vị trí target liên kết ở bên trái của một biểu thực liên kết phải là một input hay một output. Hãy nhớ rằng, một Component cũng là một Directive. Hay nói cách khác: *Target của một directive binding bắt buôc phải là một input hoặc một output.
Trong phần này, chúng ta tập trung vào việc liên kết với các thành viên Component trong các biểu thức template bên phải của phần khai báo liên kết. Một thành viên trong vị trí đó có thể coi như một “nguồn dữ liệu” (data source) liên kết. Nó không phải là một target cho sự liên kết. Trong ví dụ dưới đây, iconUrl
và onSave
là các thành viên của một Component. Cả hai đều được tham chiếu với các biểu thức template phía bên phải:
Chúng không phải là input hay output của Component mà phục vụ như data source cho các liên kết của chúng.
Bây giờ nhìn vào HeroDetailComponent
khi nó là target của một liên kết:
Cả HeroDetail.hero
và HeroDetail.deleted
đều ở bên trái của các biểu thức liên kết.
HeroDetail.hero
là target của một Property Binding, HeroDetail.deleted
là target của một Event Binding.
Luồng dữ liệu vào HeroDetail.hero
target property từ biểu thức template nên HeroDetail.hero
là một input property từ góc nhìn của HeroDetail
.
Luồng ra các sự kiện của HeroDetail.deleted
target property và hướng tới nơi nhận trong biểu thức template nên HeroDetail.deleted
là một output property từ góc nhìn của HeroDetail
.
Khi chúng ta nhìn vào bên trong HeroDetailComponent
chúng ta thấy rằng các property này được đánh dấu như các input và output property: