簡體   English   中英

如何將 `trackBy` 與 `ngFor` 一起使用

[英]How to use `trackBy` with `ngFor`

我真的不明白我應該從trackBy返回什么。 根據我在網上看到的一些示例,我應該返回對象上某些屬性的值。 這樣對嗎? 為什么要獲取index作為參數?

例如,在以下情況下:

組件.component.ts

constructor() {
    window.setInterval(() => this.users = [
            { name: 'user1', score: Math.random() },
            { name: 'user2', score: Math.random() }
        ],
        1000);
}

userByName(index, user) {
    return user.name;
}

組件.template.html

<div *ngFor="let user of users; trackBy:userByName">
  {{user.name}} -> {{user.score}}
</div>

盡管名稱未更改,但此模板中顯示的對象仍會更新。 為什么?

在為ngForOf指令觸發的每個ngDoCheck上,Angular 都會檢查哪些對象發生了變化。 它在這個過程中使用了差異,每個差異都使用trackBy函數將當前對象與新對象進行比較。 默認trackBy函數按身份跟蹤項目:

const trackByIdentity = (index: number, item: any) => item;

它接收當前項目並且應該返回一些值。 然后將函數返回的值與此函數上次返回的值進行比較。 如果值發生變化,diff 報告變化。 因此,如果默認函數返回對象引用,則如果對象引用發生更改,它將與當前項不匹配。 因此,您可以提供將返回其他內容的自定義trackBy函數。 比如對象的一些鍵值。 如果此鍵值與前一個鍵值匹配,則 Angular 將不會檢測到更改。

語法...trackBy:userByName不再受支持。 您現在必須提供函數引用。 這是基本示例:

setInterval( () => {
  this.list.length = 0;
  this.list.push({name: 'Gustavo'});
  this.list.push({name: 'Costa'});
}, 2000);

@Component({
  selector: 'my-app',
  template: `
   <li *ngFor="let item of list; trackBy:identify">{{item.name}}</li>
  `
})
export class App {
  list:[];

  identify(index, item){
     return item.name; 
  }

盡管對象引用發生了變化,但 DOM 並未更新。 這是笨蛋。 如果您好奇ngFor如何在后台工作, 請閱讀此答案

由於這個話題仍然很活躍,而且很難找到一個明確的答案,所以除了@Max的答案之外,我還要添加幾個例子:

app.component.ts

array = [
    { "id": 1, "name": "bill" },
    { "id": 2, "name": "bob" },
    { "id": 3, "name": "billy" }
]

foo() {
    this.array = [
        { "id": 1, "name": "foo" },
        { "id": 2, "name": "bob" },
        { "id": 3, "name": "billy" }
    ]
}

identify(index, item) {
    return item.id;
}

讓我們使用*ngForarray顯示為 3 個 div。

app.component.html

沒有 trackBy*ngFor示例:

<div *ngFor="let e of array;">
   {{e.id}} - {{e.name}}
</div>
<button (click)="foo()">foo</button>

如果我們點擊foo按鈕會發生什么?

→ 3 個 div 將被刷新。 自己嘗試一下,打開你的控制台進行驗證。

帶有 trackBy*ngFor示例:

<div *ngFor="let e of array; trackBy: identify">
   {{e.id}} - {{e.name}}
</div>
<button (click)="foo()">foo</button>

如果我們點擊foo按鈕會發生什么?

→ 只有第一個 div 會被刷新。 自己嘗試一下,打開你的控制台進行驗證。

如果我們更新第一個對象而不是整個對象呢?

foo() {
    this.array[0].name = "foo";
}

→ 這里不需要使用trackBy

它在使用通常看起來像我用array模式化的Subscription時特別有用。 所以它看起來像:

 array = [];
 subscription: Subscription;

 ngOnInit(): void {
    this.subscription = this.fooService.getArray().subscribe(data => {
       this.array = data;
    });
 }

 identify(index, item) {
    return item.id;
 }

從文檔中:

為避免這種昂貴的操作,您可以自定義默認跟蹤算法。 通過向 NgForOf 提供 trackBy 選項。 trackBy 接受一個有兩個參數的函數:index 和 item。 如果給出了 trackBy,Angular 會根據函數的返回值跟蹤變化。

在此處閱讀更多信息: https ://angular.io/api/common/NgForOf

在這里找到我的原始答案: https ://stackoverflow.com/a/57890227/9753985

這是我在我的項目中使用的,以允許通過迭代模型的屬性進行跟蹤,而無需在組件的類中編寫函數:

import { Host, Directive, Input } from "@angular/core";
import { NgForOf } from "@angular/common";

@Directive({
    selector: "[ngForTrackByProperty]"
})
export class TrackByPropertyDirective {

    private _propertyName: string = "";

    public constructor(@Host() private readonly _ngFor: NgForOf<any>) {
        this._ngFor.ngForTrackBy = (_: number, item: any) => this._propertyName ? item[this._propertyName] : item;
    }

    @Input("ngForTrackByProperty")
    public set propertyName(value: string | null) {
        // We must accept null in case the user code omitted the ": 'somePropName'" part.
        this._propertyName = value ?? "";
    }

}

用法 :

<some-tag *ngFor="let item of models; trackByProperty: 'yourDiscriminantProp'">

app.component.html

<button class="btn btn-warning" (click)="loadCourses()">LoadCourses</button>
<ul>
    <li *ngFor="let course of courses; trackBy:trackCourse">
        {{course.name}}
    </li>
</ul>

app.component.ts

loadCourses() {

    this.courses = [
    {id:1, name:'cour1'},
    {id:2, name:'cour2'},
    {id:3, name:'cour3'}
    ]  
};

trackCourse(index : number, course: any) {
    return course ? course.id : undefined;
};

Mosh參考代碼您可以在指令部分找到

使用 trackBy 的目的是在可迭代對象中設置元素的標識。 如果 Angular 看到兩個具有相同標識的元素,它將繼續檢查元素的內容,並且只有在內容發生更改時才會重新繪制。 如果沒有標識,Angular 將依賴元素的對象引用,即使內容相同,通常也會發生變化,因此 Angular 會因為引用不同而重新繪制元素。

這個有角度NgFor文檔將為您提供幫助。 https://angular.io/docs/ts/latest/api/common/index/NgFor-directive.html

以下示例為您的代碼

<div *ngFor="let user of users; trackBy:user?.name">
 {{user.name}} -> {{user.score}}
</div>

否則你可以使用

*ngFor="a of array; index as i;"

[attr.data-target]="'#test' + i"

name="test{{i}}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM