Angular.js:TrackBy 不会阻止完整的 *ngFor 生成的列表刷新
Angular.js: TrackBy doesn't prevent full *ngFor-generated list refresh
我有代码,可以在 ul 列表中打印一个简单句子的单词,这样您就可以删除任何项目,然后再将其插入:
arr = ['hi', 'i', 'am', 'an' ,'item', 'too'].map(function (word) {
return {value: word}
});
trackByFn (item : any) {
return item.value;
}
removed : any = null;
removed_i : any = null;
removeItem (i : number) {
this.removed_i = i;
this.removed = this.arr.splice(i, 1);
}
insert () {
this.arr.splice(this.removed_i, 0, this.removed);
}
<ul>
<li *ngFor="let item of arr; let i = index; trackBy: trackByFn">
{{item.value}}
<button *ngIf="!removed" (click)="removeItem(i)">Remove</button>
</li>
</ul>
<button *ngIf="removed" (click)="insert()">Insert</button>
它导致了 2 个问题:
- 我尝试使用 trackBy 功能。我的 trackByFn returns item.value 作为 "id" 但仍然当在调用 insert(...) 后重新呈现列表时,总是有几个 li-s 更新(通过浏览器控制台检查)!
- 而且插入的item不包含{{item.value}},是插进去的!
拜托,有人能解释一下这是怎么回事吗?
更新
我更改了 trackBy-function 并添加了一个带有 full_refresh() 反应的按钮。
<button (click)="full_refresh()">Random refresh</button>
full_refresh () {
var prevSource = this.source, prevLength = prevSource.length;
var source = this.source = new Array(prevLength);
for (let i = 0; i < prevLength; i++)
source[i] = {value: prevSource[i].value + prevSource[i].value};
}
trackBy (i : number, word : any) {
return i; // UPDATE 3: corrected according to Siddhant's note
}
所以,现在我已经更改了 trackBy-function(现在它 returns index 有目的)并添加了一个完整的刷新按钮,每个按钮都将 旧数组 和 数组对象 替换为 全新的 。所以,新的数组对象与旧的无关,所以==应该returnfalse。那么 Angular 应该检查 trackBy-function,对吧?而trackBy-functionreturns数组index,所以每个new object都应该被识别与数组中相应的 old object “相同”,对吧?但我仍然完全刷新了元素!而且,控制台中会弹出一个错误:
无法绑定到 'ngForNgTrackBy',因为它不是 'li' 的已知 属性。
更新 2:
刚刚发现 trackBy 函数根本没有被调用。当我将 console.log(...) 添加到其中时,当我提交 full_refresh 时,不会记录任何内容,这会更改 source ,然后通过 ngFor.
打印为列表
此外,当我像这样更改函数名称时(请参阅 ngTrackBy: trackByByeByeBye 在 ngFor 语句的末尾),应用程序已编译,但在控制台中出现错误:无法绑定到 'ngForNgTrackBy' 因为它不是 'li' 的已知 属性。
<ul>
<li *ngFor="let word of source; let i = index; trackBy: trackByByeByeBye">
<!--there should be trackBy <<actual property>> : trackBy <<corresponding method>>-->
{{word.value}}
<button (click)="remove(i)">Remove</button>
</li>
</ul>
回答
请看Saddhant回复的最后一条评论!它在那里! :) 您在上面看到了代码,它已经被更正了!祝您愉快! :)
{{item.value}}
插入后不打印值的原因是因为插入的值是一个数组而不是包含 value
属性.
的对象
// splice returns an array of deleted elements
this.removed = this.arr.splice(i, 1);
// this.removed would be [{value: 'deletedValue'}]
// You can use this.removed[0] when inserting
this.arr1.splice(this.removed_i, 0, this.removed[0]);
// In case if this.removed contains multiple elements, you can use spread operator as ...this.removed
在您的示例中,使用 trackBy
并没有任何实际帮助,也不是必需的,因为您正在改变相同的数组并使用相同的对象引用。由于数组中的对象引用保持不变,Angular 自动将能够处理不必要的 re-creation 个 DOM 个节点。
此外,在您的案例中编写的 trackByFn
将无法按预期工作,因为传递给 trackByFn
的第一个参数是索引。
编写函数的正确方法是:
trackByFn (index: number, item : any) {
return item.value;
}
编辑:为新问题添加到post
由于您现在正在创建一个带有新引用的新数组,因此在这种情况下使用 trackBy
是有意义的。
关于元素刷新的原因?
您在 trackByFn 中使用 word.value
,并且由于您在新对象中更改了 value
,Angular 会将其视为新记录,re-create 元素。
// The 'value' which is being used in trackByFn is now changed
{value: prevSource[i].value + prevSource[i].value}
我有代码,可以在 ul 列表中打印一个简单句子的单词,这样您就可以删除任何项目,然后再将其插入:
arr = ['hi', 'i', 'am', 'an' ,'item', 'too'].map(function (word) {
return {value: word}
});
trackByFn (item : any) {
return item.value;
}
removed : any = null;
removed_i : any = null;
removeItem (i : number) {
this.removed_i = i;
this.removed = this.arr.splice(i, 1);
}
insert () {
this.arr.splice(this.removed_i, 0, this.removed);
}
<ul>
<li *ngFor="let item of arr; let i = index; trackBy: trackByFn">
{{item.value}}
<button *ngIf="!removed" (click)="removeItem(i)">Remove</button>
</li>
</ul>
<button *ngIf="removed" (click)="insert()">Insert</button>
它导致了 2 个问题:
- 我尝试使用 trackBy 功能。我的 trackByFn returns item.value 作为 "id" 但仍然当在调用 insert(...) 后重新呈现列表时,总是有几个 li-s 更新(通过浏览器控制台检查)!
- 而且插入的item不包含{{item.value}},是插进去的!
拜托,有人能解释一下这是怎么回事吗?
更新
我更改了 trackBy-function 并添加了一个带有 full_refresh() 反应的按钮。
<button (click)="full_refresh()">Random refresh</button>
full_refresh () {
var prevSource = this.source, prevLength = prevSource.length;
var source = this.source = new Array(prevLength);
for (let i = 0; i < prevLength; i++)
source[i] = {value: prevSource[i].value + prevSource[i].value};
}
trackBy (i : number, word : any) {
return i; // UPDATE 3: corrected according to Siddhant's note
}
所以,现在我已经更改了 trackBy-function(现在它 returns index 有目的)并添加了一个完整的刷新按钮,每个按钮都将 旧数组 和 数组对象 替换为 全新的 。所以,新的数组对象与旧的无关,所以==应该returnfalse。那么 Angular 应该检查 trackBy-function,对吧?而trackBy-functionreturns数组index,所以每个new object都应该被识别与数组中相应的 old object “相同”,对吧?但我仍然完全刷新了元素!而且,控制台中会弹出一个错误:
无法绑定到 'ngForNgTrackBy',因为它不是 'li' 的已知 属性。
更新 2:
刚刚发现 trackBy 函数根本没有被调用。当我将 console.log(...) 添加到其中时,当我提交 full_refresh 时,不会记录任何内容,这会更改 source ,然后通过 ngFor.
打印为列表此外,当我像这样更改函数名称时(请参阅 ngTrackBy: trackByByeByeBye 在 ngFor 语句的末尾),应用程序已编译,但在控制台中出现错误:无法绑定到 'ngForNgTrackBy' 因为它不是 'li' 的已知 属性。
<ul>
<li *ngFor="let word of source; let i = index; trackBy: trackByByeByeBye">
<!--there should be trackBy <<actual property>> : trackBy <<corresponding method>>-->
{{word.value}}
<button (click)="remove(i)">Remove</button>
</li>
</ul>
回答
请看Saddhant回复的最后一条评论!它在那里! :) 您在上面看到了代码,它已经被更正了!祝您愉快! :)
{{item.value}}
插入后不打印值的原因是因为插入的值是一个数组而不是包含 value
属性.
// splice returns an array of deleted elements
this.removed = this.arr.splice(i, 1);
// this.removed would be [{value: 'deletedValue'}]
// You can use this.removed[0] when inserting
this.arr1.splice(this.removed_i, 0, this.removed[0]);
// In case if this.removed contains multiple elements, you can use spread operator as ...this.removed
在您的示例中,使用 trackBy
并没有任何实际帮助,也不是必需的,因为您正在改变相同的数组并使用相同的对象引用。由于数组中的对象引用保持不变,Angular 自动将能够处理不必要的 re-creation 个 DOM 个节点。
此外,在您的案例中编写的 trackByFn
将无法按预期工作,因为传递给 trackByFn
的第一个参数是索引。
编写函数的正确方法是:
trackByFn (index: number, item : any) {
return item.value;
}
编辑:为新问题添加到post
由于您现在正在创建一个带有新引用的新数组,因此在这种情况下使用 trackBy
是有意义的。
关于元素刷新的原因?
您在 trackByFn 中使用 word.value
,并且由于您在新对象中更改了 value
,Angular 会将其视为新记录,re-create 元素。
// The 'value' which is being used in trackByFn is now changed
{value: prevSource[i].value + prevSource[i].value}