NgForOf
NgForOf directive lặp qua một tập hợp dữ liệu như mảng (array), danh sách (list),... và tạo một phần tử HTML cho từng mục từ một HTML template. Nó giúp chúng ta xây dựng danh sách hoặc bảng để hiển thị dữ liệu dạng bảng một cách đẹp mắt.
Syntax với dấu *:
<html-element *ngFor="let <item> of <items>;"><html-Template></html-Template></html-element>
- <html-element> là phần tử mà chúng ta áp dụng ngFor directive. Nó lặp lại <html-element>...</html-element> cho mỗi phần tử (item) của mảng items.
- Syntax NgForOf bắt đầu với *ngFor. Dấu * đại diện cho Angular Template Syntax.
- let <item> of <items>;
- item là Template Input Variable. Nó đại diện giá trị (item) được lặp hiện tại từ <items>. <items> là một hợp mà chúng ta cần hiện thị ra giao diện. Nó thường là một property trên component class và có thể là bất cứ thứ gì mà bạn có thể lặp (thường là một mảng - array).
- Phạm vi của item chỉ nằm trong <html-element>...</html-element>. Bạn có thể truy cập nó ở bất cứ đâu trong đó.
Ví dụ:
<ol><li *ngFor="let movie of movies;">{{movie.title}} - {{movie.releaseDate}}</li></ol>
Local Variables
ngFor gán một số value vào các local variables cho phép chúng ta truy xuất chúng ở mỗi vòng lặp để tinh chỉnh việc hiển thị như sau:
Danh sách các giá trị đã xuất được cung cấp bởi ngFor directive:
- $implicit: T là giá trị của phần tử trong danh sách ở lần lặp hiện tại.
<li *ngFor="let movie of movies;">// movie chính là $implicit
- index: number là index của phần tử hiện tại trong mảng
- count: number là số lượng phần tử có trong mảng
- first: boolean là true nếu phần tử hiện tại là phần tử đầu tiên của mảng
- last: boolean là true nếu phần tử hiện tai là phần tử cuối cùng của mảng
- even: boolean là true nếu phần tử hiện tại có index chẵn
- odd: boolean là true nếu phần tử hiện tại có index lẻ
Cách sử dụng:
Bạn tạo ra một biến (i) và dùng let để khai báo và gán biến đó bằng local variable ở trên:
let i=index
Ví dụ:
<ol><li *ngFor="let movie of movies; let i = index;">Movie {{i}} - {{movie.title}} - {{movie.releaseDate}}</li></ol>
Hoặc bạn có thể sử dụng as như sau:
index as i
Ví dụ:
<ol><li *ngFor="let movie of movies; index as i;">Movie {{i}} - {{movie.title}} - {{movie.releaseDate}}</li></ol>
Track By
Angular ngFor directive bao gồm một trackBy function cho phép chúng ta chỉ định khóa riêng để xác định đối tượng.
Vấn đề:
Angular sử dụng object identity để so sánh các phần tử trong danh sách hiển thị với các DOM node. Do đó, khi bạn thêm hoặc xóa một item, Angular sẽ theo dõi nó và chỉ cập nhật các item đã sửa đổi trong DOM. Nó không render lại toàn bộ danh sách.
Nhưng điều này sẽ thất bại nếu chúng ta cập nhật danh sách từ backend server. Đó là vì các đối tượng được lấy từ server không thể so sánh với các đối tượng hiện có trong danh sách vì tham chiếu đã thay đổi. Angular chỉ đơn giảm là xóa các phần từ này khỏi DOM và tạo lại các phần tử mới từ dữ liệu mới. Điều này sẽ ảnh hưởng rất lớn đến hiệu suất.
Giải pháp:
Angular ngFor cung cấp một trackBy function để nói với angular cách xác định các phần tử tương tự nhau. Angular sẽ sử dụng giá trị được trả về bởi trackBy function để khớp (match) với các phần tử được lấy từ backend server và cập nhật các phần tử DOM mà không cần tạo lại chúng.
Chúng ta phải luôn chỉ định primary key hoặc unique key để làm giá trị trả về của trackBy function.
Ví dụ:
// file .html<tr *ngFor="let movie of movies; trackBy : trackByFn;"><td>{{movie.title}}</td><td>{{movie.director}}</td><td>{{movie.cast}}</td><td>{{movie.releaseDate}}</td></tr>
- Tạo trackByFn function. trackByFn function nhận vào index và item hiện tại như một tham số và trả về unique id.
trackByFn(index, item) {return item.title;}
- Tìm hiểu chi tiết về ngFor trackBy tại đây.
Sử dụng cấu trúc ngFor và ng-template
Thay vì sử dụng syntax NgForOf với dấu *, chúng ta có thể sử dụng dụng syntax ngFor với ng-template và property binding như sau:
<ol><ng-templatengFor[ngForOf]="movies"let-movie // đây chính là biến implicit ngầm hiểu cho item của vòng lặp hiện tạilet-i="index"[ngForTrackBy]="trackByFn"><li>Movie {{i}} - {{movie.title}} - {{movie.releaseDate}}</li></ng-template></ol>
Sử dụng nhiều structural directive trên cùng một phần tử
Trong nhiều trường hợp, chúng ta có thể cần kiểm tra một dữ liệu nào đó trong vòng lặp. Tuy nhiên, nếu chúng ta đặt ngIf và ngFor trên cùng một phần tử thì nó sẽ báo lỗi và không work.
Do đó, để sử dụng ngIf và ngFor chúng ta tách nó ra 2 tầng element và bọc phần tử chứa ngIf trong phần tử chứa ngFor:
<div *ngFor="let movie of movies; let i = index"><div *ngIf="i % 2 === 0">Movie {{i}} - {{movie.title}} - {{movie.releaseDate}}</div></div>
Nhưng nếu làm như trên, nó sẽ sinh ra một thẻ div dư thừa. Để giải quyết, chúng ta có thể sử dụng ng-container hoặc cấu trúc ngIf với ng-template:
- ng-template:
<div *ngFor="let movie of movies; let i = index"><ng-template [ngIf]="i % 2 === 0">Movie {{i}} - {{movie.title}} - {{movie.releaseDate}}</ng-template></div>
- ng-container:
<div *ngFor="let movie of movies; let i = index"><ng-container *ngIf="i % 2 === 0">Movie {{i}} - {{movie.title}} - {{movie.releaseDate}}</ng-container></div>
Example Code
Reference
Tags:
angular