Lập trình Angular - Bài 4.2: Structural Directive - ngFor

Lập trình Angular - Bài 4.2: Structural Directive - ngFor

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>;
    • itemTemplate 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 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-templateproperty binding như sau:
<ol>
  <ng-template
    ngFor
    [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ại
    let-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 ngIfngFor trên cùng một phần tử thì nó sẽ báo lỗi và không work.
Do đó, để sử dụng ngIfngFor 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

Đăng nhận xét

Mới hơn Cũ hơn