Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to maintain vertical scroll when updating Angular 5 data table?

I'd like to frequently update my data table (Covalent td-data-table with several ng-template) with new data pulled from a JSON REST API. More rows than will fit on browser so user may need to scroll vertically. But when I update the data in the table it redraws completely i.e. the vertical scroll position resets to the top, tool tips flash, etc..

Hacks to e.g. save/restore the vertical scroll such as below kind of work but they create a lot of visual mess, especially in 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;
  }
});

How can I cleanly update the data in a table (or any component really) without hacking to reset scroll positions & putting up with a lot of flashing behaviour?

If there is no solution using the Covalent data table, is there another Angular 2+ control that handles this properly?

Animated screen capture of problem: Vertical scroll snaps back when data is updated. Vertical scroll should be maintained across data updates. screencapture

like image 633
bunt Avatar asked Apr 11 '18 08:04

bunt


1 Answers

Pushing data to the same array variable which is being used by *ngFor wont re-render html.

Simple example: https://angular-frjdnf.stackblitz.io

Edit:

I have made a sample project with covalent table using *ngFor with angular updating only the additional row and not re-rendering the whole table.

Link to 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 has the initial data that was loaded into the table and I am just pushing some dummy data on click of the "Add Data" button to the basicData for the purpose of testing the scroll bar.

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

}

Hope this helps.

like image 134
Gowtham Raj J Avatar answered Sep 27 '22 20:09

Gowtham Raj J