Search code examples
angularscrollportal

Keep scrolling status in Angular portals


I am running into a problem with angular portals: I have a page with a portal in it and some triggers that change the portal content. All good. When the portal changes its content, it keeps variables of rendered (inside the portal) component in memory, for instance an increment; but sadly not the scrolling position: each time the portal changes, scroll is back at top.

Does anyone knows how to solve this?

I reproduced the problem here StackBlitz:

  • Click on "SET TO 2"
  • Click on increment and scroll down
  • Click on "SET TO <other than 2>
  • Click on "SET TO 2"
  • See your last increment but scroll is at top and not at the place you left it

Solution

  • try this in your widget:

    import { CdkScrollable } from "@angular/cdk/overlay";
    import {
      AfterViewChecked,
      AfterViewInit,
      Component,
      ElementRef,
      Input,
      OnDestroy,
      ViewChild
    } from "@angular/core";
    
    @Component({
      selector: "tab2",
      template: `
        <portal>
          <div
            #scrollitem
            cdkScrollable
            *ngIf="target == 2"
            style="border-style: solid; height:200px; overflow: auto"
          >
            <p>Value: {{ tab2Value }}</p>
            <button (click)="increment()">INCREMENT</button>
            <p>INSIDE PORTAL: TAB2</p>
            <p>INSIDE PORTAL: TAB2</p>
            <p>INSIDE PORTAL: TAB2</p>
            <p>INSIDE PORTAL: TAB2</p>
            <p>INSIDE PORTAL: TAB2</p>
            <p>INSIDE PORTAL: TAB2</p>
            <p>INSIDE PORTAL: TAB2</p>
            <p>INSIDE PORTAL: TAB2</p>
            <p>INSIDE PORTAL: TAB2</p>
            <p>INSIDE PORTAL: TAB2</p>
            <p>INSIDE PORTAL: TAB2</p>
            <p>INSIDE PORTAL: TAB2</p>
            <p>INSIDE PORTAL: TAB2</p>
            <p>INSIDE PORTAL: TAB2</p>
            <p>INSIDE PORTAL: TAB2</p>
            <p>INSIDE PORTAL: TAB2</p>
            <p>INSIDE PORTAL: TAB2</p>
            <p>INSIDE PORTAL: TAB2</p>
            <p>INSIDE PORTAL: TAB2</p>
          </div>
        </portal>
      `,
      styles: []
    })
    export class Tab2 implements AfterViewChecked, OnDestroy {
      @Input() target: number;
    
      @ViewChild("scrollitem", { read: CdkScrollable }) scrollitem: CdkScrollable;
    
      tab2Value = 0;
    
      scrolltop = 0;
    
      subs = null;
    
      constructor() {}
      ngOnDestroy(): void {
        if (this.subs) {
          this.subs.unsubscribe();
        }
      }
    
      ngAfterViewChecked(): void {
        if (this.scrollitem) {
          this.scrollitem.scrollTo({ top: this.scrolltop });
    
          this.subs = this.scrollitem.elementScrolled().subscribe(o => {
            this.scrolltop = this.scrollitem.measureScrollOffset("top");
          });
        }
      }
    
      increment() {
        this.tab2Value++;
      }
    }
    

    Stackblitz here : https://stackblitz.com/edit/angular-ivy-2uaxdz?file=src%2Fapp%2Ftab2.component.ts