Material Angular scroll to element on mat-list

Issue

I have an Angular Material list with multiple elements, where one of them can be selected.

When I load this list I want to scroll list up to that selected element, to make it more visible. Is there any option?

I was thinking about something like in ngOnInit to check if item is selected, but I have really no idea how to scroll this list up to that item.

Note: not whole page should be scrolled, only the elements on the list.

component.html

    <mat-nav-list>
        <mat-list-item *ngFor="let item of items" (click)="itemClick(item)"
            [ngClass]="item.selectedClass">
            {{item.someValue}}
        </mat-list-item>
    </mat-nav-list>

component.ts

    private itemClick(item: Item): Observable<any> {
        if (item) {
            this.something = item;
            this.items.forEach(item => {
                if (item && item.name === item.name) {
                    item["selectedClass"] = "item-selected";
                } else {
                    item["selectedClass"] = undefined;
                }
            });
        } else {
            this.something = null;
            this.items.forEach(item => {
                    item["selectedClass"] = undefined;
            });
        }

        return of(null);
    }

Solution

Check out this demo: https://stackblitz.com/edit/angular-yjbpoq?file=src%2Fapp%2Fhello.component.ts

The gist is that you attach a new directive to your list and to each of your list items:

<mat-list appScrollable ...>
  <mat-list-item appOffsetTop ...>
  ...
  </mat-list-item>
</mat-list>

The one on the list can set the scrollTop (or use scrollTo, or whatever) for the list:

@Directive({selector: '[appScrollable]'})
export class ScrollableDirective {
  constructor(private _el: ElementRef) {}
  set scrollTop(value: number) { this._el.nativeElement.scrollTop = value; }
}

And the one on the list items can report their offsetTops:

@Directive({ selector: '[appOffsetTop]'})
export class OffsetTopDirective {
  constructor(private _el: ElementRef) { }
  get offsetTop(): number { return this._el.nativeElement.offsetTop; }
}

Each type of directive is accessed in the component via @ViewChild and @ViewChildren:

  @ViewChildren(OffsetTopDirective) listItems: QueryList<OffsetTopDirective>;
  @ViewChild(ScrollableDirective) list: ScrollableDirective;

In the AfterViewInit life cycle hook, the list of items is searched for the one that matches the current selected item (set randomly here for illustration purposes). The list’s scrollTop is then set to that item’s offsetTop:

  // Inside your component
  selectedItem = Math.floor(Math.random() * 500);
  items = new Array(500).fill(0).map((_, i) => `Item ${i}`);

  ngAfterViewInit() {
    this.list.scrollTop = this.listItems.find((_, i) => i === this.selectedItem).offsetTop;
  }

Answered By – night_owl

Answer Checked By – Robin (AngularFixing Admin)

Leave a Reply

Your email address will not be published.