Angular8 trackby 没有正确更新
Angular8 trackby not updating correctly
首先介绍一下背景。我正在开发一个使用 agm 地图实时显示多个用户位置的应用程序。我最近一直致力于提高性能,因为可能有成千上万的用户,每个用户代表一个地图标记或 dom 元素。我已经将父组件和子组件更改为都使用 onpush 策略,子组件使用 ngFor 循环渲染地图标记,我还在这个循环中添加了 trackBy。位置更新是通过 signalr 接收的,对象已正确更新,但即使在更改检测 运行 之后,地图上的标记位置也未正确更新。
下面的两个组件是缩减版本以帮助识别问题。
首先是Parent Map组件
@Component({
selector: 'app-map',
template: '<agm-map>
<app-map-markers #mapMarkers [users]="users"></app-map-markers>
</agm-map>',
changeDetection: ChangeDetectionStrategy.OnPush
})
export class MapComponent implements OnInit, OnDestroy {
users: MapUser[];
private handleLocationUpdate(updatedUser: any) {
const user = this.users.find(this.findIndexToUpdate, updatedUser.Id);
if (user !== null && user !== undefined) {
user.lat = updatedUser.Lat;
user.lng = updatedUser.Lng;
user.lastSeen = updatedUser.LastSeen;
const userIndex = this.handledUsers.indexOf(user);
this.handledUsers[userIndex] = user;
}
}
findIndexToUpdate(newItem) {
return newItem.id === this;
}
}
用户数组是通过订阅 http 服务来填充的,但我没有考虑它,因为它工作正常。当接收到位置更新时,它由父级处理,我留下了显示此功能的功能,但再次通过订阅信号器服务调用该功能。
现在是地图标记组件。
@Component({
selector: 'app-map-markers',
template: '<agm-marker *ngFor="let user of users; let i = index; trackBy: trackByUser" [latitude]="user.lat" [longitude]="user.lng" [title]="user.firstName">
</agm-marker>',
changeDetection: ChangeDetectionStrategy.OnPush
})
export class MapMarkersComponent {
@Input() users: MapUser[];
constructor() {}
public trackByUser(indx: number, value: MapUser) {
let identifier: string = indx.toString();
if ('lat' in value && 'lng' in value) {
identifier = identifier.concat(value.lastSeen.toString());
}
return identifier;
}
}
我怀疑问题是由于 onpush 没有检测到变化,但是出于性能原因,onpush 似乎是这种情况下的最佳策略。我只需要更新元素而不是重新绘制整个用户列表。但也许我的电线交叉了,因为我对 angular 还是很陌生。感谢您的任何帮助或建议。
尝试调用变更检测 'markForCheck()',这将在下一个循环中进行 angular 到 运行 的变更检测。
@Component({
selector: 'app-map-markers',
template: '<agm-marker *ngFor="let user of users; let i = index; trackBy: trackByUser" [latitude]="user.lat" [longitude]="user.lng" [title]="user.firstName">
</agm-marker>',
changeDetection: ChangeDetectionStrategy.OnPush
})
export class MapMarkersComponent {
public users: MapUser[]
@Input()
set users(values: any[]){
this.users = values;
this.cdr.markForCheck();
}
constructor(private cdr: changeDetectorRef) {}
public trackByUser(indx: number, value: MapUser) {
let identifier: string = indx.toString();
if ('lat' in value && 'lng' in value) {
identifier = identifier.concat(value.lastSeen.toString());
}
return identifier;
}
}
首先介绍一下背景。我正在开发一个使用 agm 地图实时显示多个用户位置的应用程序。我最近一直致力于提高性能,因为可能有成千上万的用户,每个用户代表一个地图标记或 dom 元素。我已经将父组件和子组件更改为都使用 onpush 策略,子组件使用 ngFor 循环渲染地图标记,我还在这个循环中添加了 trackBy。位置更新是通过 signalr 接收的,对象已正确更新,但即使在更改检测 运行 之后,地图上的标记位置也未正确更新。
下面的两个组件是缩减版本以帮助识别问题。
首先是Parent Map组件
@Component({
selector: 'app-map',
template: '<agm-map>
<app-map-markers #mapMarkers [users]="users"></app-map-markers>
</agm-map>',
changeDetection: ChangeDetectionStrategy.OnPush
})
export class MapComponent implements OnInit, OnDestroy {
users: MapUser[];
private handleLocationUpdate(updatedUser: any) {
const user = this.users.find(this.findIndexToUpdate, updatedUser.Id);
if (user !== null && user !== undefined) {
user.lat = updatedUser.Lat;
user.lng = updatedUser.Lng;
user.lastSeen = updatedUser.LastSeen;
const userIndex = this.handledUsers.indexOf(user);
this.handledUsers[userIndex] = user;
}
}
findIndexToUpdate(newItem) {
return newItem.id === this;
}
}
用户数组是通过订阅 http 服务来填充的,但我没有考虑它,因为它工作正常。当接收到位置更新时,它由父级处理,我留下了显示此功能的功能,但再次通过订阅信号器服务调用该功能。
现在是地图标记组件。
@Component({
selector: 'app-map-markers',
template: '<agm-marker *ngFor="let user of users; let i = index; trackBy: trackByUser" [latitude]="user.lat" [longitude]="user.lng" [title]="user.firstName">
</agm-marker>',
changeDetection: ChangeDetectionStrategy.OnPush
})
export class MapMarkersComponent {
@Input() users: MapUser[];
constructor() {}
public trackByUser(indx: number, value: MapUser) {
let identifier: string = indx.toString();
if ('lat' in value && 'lng' in value) {
identifier = identifier.concat(value.lastSeen.toString());
}
return identifier;
}
}
我怀疑问题是由于 onpush 没有检测到变化,但是出于性能原因,onpush 似乎是这种情况下的最佳策略。我只需要更新元素而不是重新绘制整个用户列表。但也许我的电线交叉了,因为我对 angular 还是很陌生。感谢您的任何帮助或建议。
尝试调用变更检测 'markForCheck()',这将在下一个循环中进行 angular 到 运行 的变更检测。
@Component({
selector: 'app-map-markers',
template: '<agm-marker *ngFor="let user of users; let i = index; trackBy: trackByUser" [latitude]="user.lat" [longitude]="user.lng" [title]="user.firstName">
</agm-marker>',
changeDetection: ChangeDetectionStrategy.OnPush
})
export class MapMarkersComponent {
public users: MapUser[]
@Input()
set users(values: any[]){
this.users = values;
this.cdr.markForCheck();
}
constructor(private cdr: changeDetectorRef) {}
public trackByUser(indx: number, value: MapUser) {
let identifier: string = indx.toString();
if ('lat' in value && 'lng' in value) {
identifier = identifier.concat(value.lastSeen.toString());
}
return identifier;
}
}