有没有办法在数据更新时保留 PrimeNG table 滚动位置?

Is there a way to preserve PrimeNG table scroll position on data update?

我目前正在使用 Angular 12 使用 PrimeNG 12 tables 进行一个项目,但是我在 Angular 的变化检测和table 更新,特别是试图阻止 PrimeNG p-table 在数据刷新时滚动回顶部。

对于某些背景,我有一个服务(让我们称之为 LocationService)负责保存位置列表并每隔 500 毫秒更新一次。类似于以下内容:

location.ts

interface Location {
    id: string;
    lat: number;
    lng: number
}

location.service.ts

// RxJS Behaviour Subjects
_locationUpdates$: BehaviorSubject<Location[]> = new BehaviorSubject<Location[]>([]);

// RxJS Observables
locationUpdates$: Observable<Location[]> = _locationUpdates.asObservable();

// Service Function
startUpdates() {

    // Called within a service
    timer(0, 500).subscribe(() => {
        this._locations.forEach(location => {
            location.lat = // ... some method to calculate new latitude
            location.lng = // ... some method to calculate new longitude
        });

        this._locationUpdates$.next(this._locations);
    });
}

现在,我有一个组件可以订阅位置更新并将它们显示为 p-table 中的 (Lat, Lng),但还要求经度在 -45.0 和 45.0 之间学位(其他组件可能没有此要求)。滚动顺序无关紧要,尽管大多数时候它的顺序相同,因为行变化不大。

我目前是这样订阅的,使用虚拟滚动,因为可能有数百行:

位置-component.html

<ng-container *ngIf="locations$ | async as locations">
    <p-table
        dataKey="id"
        scrollHeight="flex"
        [rows]="100"
        [scrollable]="true"
        [value]="locations"
        [virtualScroll]="true"
        [virtualRowHeight]="34"
    >
        <ng-template pTemplate="header">
            <tr>
                <th>Latitude</th>
                <th>Longitude</th>
            </tr>
        </ng-template>

        <ng-template pTemplate="body" let-location>
            <tr>
                <td>{{ location.lat }}</td>
                <td>{{ location.lng }}</td>
            </tr>
        </ng-template>
    </p-table>
</ng-container>

位置-component.ts

// RxJS observables
locations$: Observable<Location[]>;

// Constructor
constructor(private _locationService: LocationService) {
    locations$ = _locationService.locationUpdates$
        .pipe(
            map(locations => locations.filter(location => location.lng >= -45.0 && location.lng <= 45.0))
        )
}

这里的问题是,当我 运行 代码时,每次刷新数据时 table 都会跳到顶部。我认为这是由于数组引用发生了变化。但是,如果我离开 async 管道并使用本地数组,只需在更新时修改它,如下所示:

.subscribe(locations => {
    this._locations.splice(0, this._locations.length, ...locations.filter(location => location.lng >= -45.0 && location.lng <= 45.0));
    this._changeDetectorRef.detectChanges();
}

Angular 没有检测到变化,即使他 detectChanges() 调用(它似乎什么也没做)。使用 this._locations = this._locations.slice() 之类的东西以另一种方式通知它只会让我回到上面的问题。

我想问大家的问题是:有人对我如何解决这个问题有任何建议吗?我的目标是每 X 毫秒刷新一次数据,但也允许用户在更新时滚动。

如有任何帮助,我们将不胜感激!

好的,所以我设法找到了这背后的罪魁祸首 - PrimeNG 的默认 table 行为是每次应用新过滤器时调用函数 resetScrollTop()

在我的例子中,我在 table 中添加了一些过滤器(文本过滤器、几个布尔过滤器等),这意味着每次更新 table 数据每隔 500 毫秒左右,就会重新应用这些过滤器,导致 table 滚动位置不断重置 (as seen in the PrimeNG source here)。

我通过简单地覆盖这个特定 table:

的函数,设法以一种有点老套的方式解决了这个问题

位置-component.html

<p-table #locationsTable ...> ... </p-table>

位置-component.ts

ngAfterViewInit() {
    if (this.locationsTable != null) {
        this.locationsTable.resetScrollTop = function() { }
    }
}

这似乎暂时解决了这个问题,但如果有人知道更好的方法,我总是乐于听取。希望这个答案对以后遇到类似情况的其他人有所帮助。