6

I've been trying to keep scroll position of element holding a grid with hundreds of rows of data. Right now it's set with overflow-y: auto. If I use router to go to different page and than go back I'd like the scroll to be in the same position. I thought that using ngAfterViewInit will do the trick but unfortunately it doesn't work. If I use the console to fire up the set position command it works fine. I guess the problem is with rows still being loaded and not yet rendered when calling ngAfterViewInit .

@Component({
  selector: 'grid',
  templateUrl: './grid.component.html',
  styleUrls: ['./grid.component.scss']
})
export class GridComponent implements OnInit, OnDestroy {

  @Input() rows: Array<Row>;
  @Input() someKindOfGridId: string;
  localGridConfigValue: ILocalGridConfigValue;

  constructor(private configService: ConfigService) { }

  ngOnInit() {
    this.localGridConfigValue = this.configService.getConfigForCurrentGrid(this.someKindOfGridId);
  }

  ngAfterViewInit(){
    document.getElementById(this.someKindOfGridId).scrollTop = this.localGridConfigValue.gridScrollTopPos;
  }

  ngOnDestroy() {
    this.localGridConfigValue.gridScrollTopPos = document.getElementById(this.someKindOfGridId).scrollTop;
  }
}

I'm still learning angular and any help will be greatly appreciated.

Regards.

cah1r
  • 1,751
  • 6
  • 28
  • 47
  • It doesn't directly solve your issue but this might help figure it out - https://stackoverflow.com/questions/39601026/angular-2-scroll-to-top-on-route-change/43627051#43627051 – mtpultz Nov 16 '17 at 20:51
  • 1
    You should look into angular's [DoCheck](https://angular.io/guide/lifecycle-hooks#docheck) lifecycle hook. It allows you to implement a custom change detection hook. In your case a hook that detects that your dom is completely loaded. – Andresson Nov 16 '17 at 21:55

2 Answers2

3

I managed to solve this using tip from @Andresson but DoCheck wasn't enough and I used ngAfterViewChecked instead. The important thing is I had to make sure that setting scroll position will be set off only once.

@Component({
  selector: 'grid',
  templateUrl: './grid.component.html',
  styleUrls: ['./grid.component.scss']
})
export class GridComponent implements OnInit, OnDestroy {

  @Input() rows: Array<Row>;
  @Input() someKindOfGridId: string;
  localGridConfigValue: ILocalGridConfigValue;
  rowsCount: number = 0;
  scrolled: boolean = false;

  constructor(private configService: ConfigService) { }

    ngOnInit() {
        this.localGridConfigValue = this.configService.getConfigForCurrentGrid(this.someKindOfGridId);

         this.scrolled = false;
    }

    ngAfterViewChecked() {
        let newRowsCount = this.rows.length;
        if (newRowsCount <= 0 || !this.rowsCountChanged(newRowsCount) || this.scrolled)
            return;

        document.getElementById(this.someKindOfGridId).scrollTop = this.localGridConfigValue.gridScrollTopPos;
        this.rowsCount = newRowsCount;
        this.scrolled = true;
    }

    private rowsCountChanged(newRowsCount: number): boolean {
        return newRowsCount !== this.rowsCount;
    }

    ngOnDestroy() {
        this.localGridConfigValue.gridScrollTopPos = document.getElementById(this.someKindOfGridId).scrollTop;
        this.scrolled = false;
    }
}
cah1r
  • 1,751
  • 6
  • 28
  • 47
0

Solution from @MateuszMigała works. We can improve it by checking for changes to the collection.

import { Component, IterableDiffers, OnInit } from '@angular/core';

@Component({
  selector: 'grid',
  templateUrl: './grid.component.html',
  styleUrls: ['./grid.component.scss']
})
export class GridComponent implements OnInit, OnDestroy {

  @Input() rows: Array<Row>;
  @Input() someKindOfGridId: string;
  localGridConfigValue: ILocalGridConfigValue;
  rowsCount: number = 0;
  scrolled: boolean = false;
  private iterableDiffer;

  constructor(private configService: ConfigService,
    private iterableDiffers: IterableDiffers) {
    this.iterableDiffer = iterableDiffers.find([]).create(null);
  }

  ngOnInit() {
    this.localGridConfigValue = this.configService.getConfigForCurrentGrid(this.someKindOfGridId);

    this.scrolled = false;
  }

  ngAfterViewChecked() {
    if (this.scrolled) {
      return;
    }
    else {
      let changes = this.iterableDiffer.diff(this.rows);
      if (!changes)
        return;

      document.getElementById(this.someKindOfGridId).scrollTop = this.localGridConfigValue.gridScrollTopPos;
      this.scrolled = true;
    }
  }


  ngOnDestroy() {
    this.localGridConfigValue.gridScrollTopPos = document.getElementById(this.someKindOfGridId).scrollTop;
    this.scrolled = false;
  }
}