Как использовать trackBy с ngFor
Я не могу понять, что я должен вернуть из trackBy
. Основываясь на примерах, которые я видел в Интернете, я должен вернуть значение некоторого свойства на объект. Правильно ли это? Почему я получаю индекс в качестве параметра?
Например, в следующем случае:
constructor() {
window.setInterval(() => this.users = [
{name: 'user1', score: Math.random()},
{name: 'user2', score: Math.random()}],
1000);
}
userByName(index, user) {
return user.name;
}
...
<div *ngFor="let user of users; trackBy:userByName">{{user.name}} -> {{user.score}}</div>
Объекты, показанные в шаблоне, все еще обновляются, несмотря на то, что имя не изменилось. Зачем?
Ответы
Ответ 1
На каждом ngDoCheck
вызванном для директивы ngForOf
Угловая проверка, какие объекты изменились. Он использует разные для этого процесса, и каждый из них использует функцию trackBy
для сравнения текущего объекта с новым. Функция trackBy
умолчанию отслеживает элементы по идентификатору:
const trackByIdentity = (index: number, item: any) => item;
Он получает текущий элемент и должен возвращать некоторое значение. Затем значение, возвращаемое функцией, сравнивается со значением, возвращаемым этой функцией в последний раз. Если значение изменится, разница изменится. Поэтому, если функция по умолчанию возвращает ссылки на объекты, она не будет соответствовать текущему элементу, если ссылка на объект изменилась. Таким образом, вы можете предоставить свою настраиваемую функцию 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
под капотом, прочитайте этот ответ.
Ответ 2
Поскольку эта тема все еще активна & найти четкий ответ сложно, позвольте мне добавить несколько примеров в дополнение к ответу @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;
}
Давайте отобразим array
на 3 деления, используя *ngFor
.
app.component.html
Пример *ngFor
без trackBy:
<div *ngFor="let e of array;">
{{e.id}} - {{e.name}}
</div>
<button (click)="foo()">foo</button>
Что произойдет, если мы нажмем кнопку foo
?
→ 3 div будут обновлены. Попробуйте сами, откройте консоль, чтобы проверить.
Пример *ngFor
с trackBy:
<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
. Так это будет выглядеть так:
array = [];
subscription: Subscription;
ngOnInit(): void {
this.subscription = this.fooService.getArray().subscribe(data => {
this.array = data;
});
}
identify(index, item) {
return item.id;
}
Из документации:
Чтобы избежать этой дорогой операции, вы можете настроить по умолчанию алгоритм отслеживания. предоставив опцию trackBy для NgForOf. trackBy принимает функцию с двумя аргументами: index и item. Если дается trackBy, angular треки изменяются на возвращаемое значение функция.
Подробнее читайте здесь: https://angular.io/api/common/NgForOf
Найдите мой оригинальный ответ здесь: fooobar.com/questions/182993/...
Ответ 3
Этот угловой документ 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>