更新Angular 5数据表时如何保持垂直滚动?
Posted
技术标签:
【中文标题】更新Angular 5数据表时如何保持垂直滚动?【英文标题】:How to maintain vertical scroll when updating Angular 5 data table? 【发布时间】:2018-09-21 01:19:50 【问题描述】:我想使用从 JSON REST API 中提取的新数据经常更新我的数据表(Covalent td-data-table
和几个 ng-template
)。多于浏览器的行数,因此用户可能需要垂直滚动。但是当我更新表中的数据时,它会完全重绘,即垂直滚动位置重置为顶部,工具提示闪烁等。
黑客攻击例如保存/恢复垂直滚动,例如以下类型的工作,但它们会造成很多视觉混乱,尤其是在 Firefox 中。
// save vertical scroll
this.scrollTop = this.tableElt.nativeElement.querySelector('.td-data-table-scrollable').scrollTop;
// update table data here
this.data = newData;
// restore vertical scroll
setImmediate(() =>
this.tableElt.nativeElement.querySelector('.td-data-table-scrollable').scrollTop = this.scrollTop;
);
我怎样才能干净地更新表(或任何组件)中的数据,而无需修改滚动位置并忍受大量闪烁行为?
如果没有使用 Covalent 数据表的解决方案,是否有另一个 Angular 2+ 控件可以正确处理这个问题?
问题的动画屏幕截图:垂直滚动在数据更新时快速回退。应在数据更新期间保持垂直滚动。
【问题讨论】:
你好,我认为你需要 ngx-infinite-scroll ,它工作得很好,你可以使用 (scrolled) 来触发你请求并获取新数据并且你不会丢失你的滚动位置,这里是一个使用它的例子:echoesplayer.com/#/search/videos 在ngFor
你应该使用列表,你使用trackBy函数吗?你是通过指数来做的吗?但你需要它给这个人。如果您将代码放在有问题的地方,它会更容易为您提供帮助。
@FatehMohamed 我不清楚如何将 ngx-infinite 与 td-data-table 一起使用。
@botika trackBy 听起来我们在正确的轨道上,但 td-data-table 没有 ngFor。我找不到任何方法将 trackBy 与 td-data-table 一起使用。
浏览光标后,<td-virtual-scroll-container [style.height.px]="400" [data]="data" [trackBy]="trackByFn">
ref covalent-nightly/virtual-scroll/ - 你能用虚拟卷轴包裹你的桌子吗?
【参考方案1】:
看起来当您获得更多数据时,您正在刷新整个列表(*ngFor 中使用的数组),因此 Angular 再次重新绘制完整表格。尝试仅将差异数据推送到该数组。
【讨论】:
1.-使用ViewChild获取“表”。 2.-在 ngDestroy 上(或在刷新表格之前)保存 naviteElement 在服务、localhost 或变量中滚动的位置。3.- 更新表格时使用此保存的变量 @Eliseo 这就是我粘贴的示例代码的作用。如前所述,它会产生大量闪烁和重绘,尤其是在 Firefox 中。【参考方案2】:我建议你切换到角度材质并将其绑定到可观察的数据源。
https://material.angular.io/components/table/overview#observable-stream-of-data-arrays
当数据源(Observable)被新数据更新时,它会更新 DOM,并且不需要跟随滚动事件。
如果您担心页面上列出的项目的数量以简单的方式支持分页; . https://material.angular.io/components/table/overview#pagination
旁注:切换到有角度的材料,因为它们的组件都有很好的文档记录,包括示例和示例代码 让我知道,以防您遇到困难。
【讨论】:
角度材质数据表非常原始,最糟糕的是它不会将表头粘贴到位,当您滚动数据时它们会滚动!这是我需要 td-data-table 的主要原因之一,它可以在滚动数据时将表头固定到位。 解决了粘头问题。 github.com/angular/material2/issues/9143 使用建议的 CSS.mat-header-row position: sticky; position: -webkit-sticky; top: 0; background-color: inherit;
谢谢我没有意识到,这可能是一个前进的方向。如果可能的话,我仍然有几十个表我想避免重写,问题是关于 td-data-table 而不是 mat-table。
最后我采用了@stevebiko 的方法并切换到原生角度材料表。工作正常,仍然支持模板,粘性解决方法效果很好。我放弃了 td-data-table,从来没有让它满意。抱歉,赏金来得太晚了。【参考方案3】:
您可以尝试使用 trackBy 函数。此函数将用于确定 *ngFor
的哪些行已更改以优化重绘。
<tbody>
<tr td-data-table-row *ngFor="let row of basicData; trackBy: getRowId">
<td td-data-table-cell *ngFor="let column of columns" [numeric]="column.numeric">
column.format ? column.format(row[column.name]) : row[column.name]
</td>
<td td-data-table-cell (click)="openPrompt(row, 'comments')">
<button mat-button [class.mat-accent]="!row['comments']">row['comments'] || 'Add Comment'</button>
</td>
</tr>
</tbody>
然后在你的打字稿中:
getRowById(index, row)
// Return some unique primitive idenitifier (string, number, etc.)
return row['some unique property'];
有关trackBy
的更多信息,请查看:
https://netbasal.com/angular-2-improve-performance-with-trackby-cc147b5104e5 https://blog.angular-university.io/angular-2-ngfor/
此外,NGX-Datatable 非常适合这个用例。它内置了虚拟滚动。 https://github.com/swimlane/ngx-datatable
【讨论】:
我使用的是 td-data-table 而不是 ngx-datatable。 trackBy 可能是 ngx-datatable 的答案,但不清楚在哪里/如何使用 td-data-table。 另外@Pearman 您的代码使用 td-data-table-row/cell/etc。所述问题使用 td-data-table 和 ng-template,因此没有 *ngFor,因此没有 trackBy。 所以,是否有另一个 Angular 2+ 控件可以正确处理这个问题 - 这没关系,但td-data-table
的正确语法不是好吗?
不确定你的意思@eric99。我很乐意使用 td-data-table-row/cell/etc。如上所示,但功能不等效。例如,没有粘性标题,没有 ng-templates,因此您不能将列混合和匹配到具有 hacking *ngIf 等的表中。但是 td-data-table 在使用垂直滚动时似乎变得平坦,因此目的的原始问题。【参考方案4】:
将数据推送到 *ngFor 正在使用的同一个数组变量不会重新渲染 html。
简单示例:https://angular-frjdnf.stackblitz.io
编辑:
我使用 *ngFor 制作了一个带有共价表的示例项目,其中仅更新附加行而不重新渲染整个表。
链接到Stackblitz.io sample project。
HTML
<button (click)="addData()">Add Data</button>
<table td-data-table>
<thead>
<tr td-data-table-column-row>
<td td-data-table-column *ngFor="let column of columns">
column.label
</td>
</tr>
</thead>
<tbody>
<tr td-data-table-row *ngFor="let row of basicData">
<td td-data-table-cell *ngFor="let column of columns">
row[column.name]
</td>
</tr>
</tbody>
</table>
TS
basicData
具有加载到表中的初始数据,我只是在单击“添加数据”按钮时将一些虚拟数据推送到basicData
,以测试滚动条。
import Component from '@angular/core';
import ITdDataTableColumn from '@covalent/core/data-table';
@Component(
selector: 'my-app',
templateUrl: './app.component.html',
styleUrls: [ './app.component.css' ]
)
export class AppComponent
columns: ITdDataTableColumn[] = [
name: 'first_name', label: 'First Name' ,
name: 'last_name', label: 'Last Name' ,
name: 'gender', label: 'Gender'
];
count = 0;
basicData: any[] = [
"first_name": "Sully",
"gender": "Male",
"last_name": "Clutterham"
,
...
]
additionalData: any[] = [
"first_name": "Sully",
"gender": "Male",
"last_name": "Clutterham"
,
...
]
addData()
if(this.count >= this.additionalData.length)
this.count = 0;
this.basicData.push(this.additionalData[this.count]);
this.count++;
希望这会有所帮助。
【讨论】:
没有 *ngFor,见基本数据表代码 (teradata.github.io/covalent/#/components/data-table)。 从给定的演示页面,如果你检查“自定义数据表”演示代码,他们有<table td-data-table>...<tr td-data-table-row *ngFor="let row of basicData">...</tr>
,他们正在将数据从 Json 文件传递到 basicData。我猜您正在遵循“基本数据表”演示结构,您只需要通过<td-data-table>
指令的输入装饰器 [data] 传递数据。您可以尝试使用“数据表(原子)组件”类型(如页面末尾所述)吗?
从“自定义数据表”代码部分,他们使用“数据表组件”,他们使用自定义属性,如td-data-table-row
、td-data-table-cell
。 <table td-data-table> <thead> ... </thead> <tbody> <tr td-data-table-row *ngFor="let row of basicData"> <td td-data-table-cell *ngFor="let column of columns"> row[column.name] </td> </tr> </tbody> </table>
我想使用原子组件,但是我失去了 td-data-table 的所有功能,例如自动粘页眉。问题专门针对具有 ng-template 子级的 td-data-table,而不是 td-data-table,td-data-table 是我工作所需要的。以上是关于更新Angular 5数据表时如何保持垂直滚动?的主要内容,如果未能解决你的问题,请参考以下文章
如何隐藏水平滚动条并保持垂直滚动条可见,同时仍然能够水平滚动?