【问题标题】:Angular8 trackby not updating correctlyAngular8 trackby 没有正确更新
【发布时间】:2019-09-01 02:26:12
【问题描述】:

所以首先是一点背景。我正在开发一个应用程序,它使用 agm 地图实时显示多个用户的位置。我最近一直致力于提高性能,因为可能有成千上万的用户,每个用户代表一个地图标记或 dom 元素。我已将父组件和子组件更改为都使用 onpush 策略,子组件使用 ngFor 循环呈现地图标记,我还在此循环中添加了 trackBy。通过信号器接收位置更新,对象被正确更新,但即使在变化检测运行之后,标记位置也没有在地图上正确更新。

以下两个组件是用于帮助识别问题的精简版本。

首先是父地图组件

     @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;
        }
    }

users 数组是通过订阅 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 中,传递 Array 作为输入指向同一个实例。尝试使用 Array.slice() 或 JSON.parse(JSON.Stringify()) 创建。
  • @SureshKumarAriya 谢谢我试一试。但这不会导致整个标记集被重绘吗?还是 trackby 会阻止它?
  • 我们可以使用 markforCheck() 手动触发 CD。将更新答案
  • @Tony_89 users 数组应该有一个新的引用来确保 OnPush 正常工作。 trackBy 将负责不重新呈现同一用户。它将确保刷新相同的 DOM 元素数据。请试一试。

标签: javascript angular typescript agm-map


【解决方案1】:

尝试调用更改检测“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;
      }
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2012-01-31
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多