从 Angular Material 选项卡内容中获取滚动位置

Get the scroll position from an Angular Material Tab content

我使用带有自定义组件的 Angular Material 选项卡组件作为选项卡的内容。 选项卡列表来自 API.

我需要获取 mat-tab-body-content 的滚动索引,因为带有此 class 的元素具有我需要的 scrollTop 值。

我可以使用以下 JS 表达式获取 scrollTop 值:

document.getElementsByClassName('mat-tab-body-content')[index];

其中 index 是选定的选项卡,mat-tab-body-content 中有滚动位置。
问题是我找不到触发的事件 更改 Tab 之前,加上这种方法是不完整的,因为不考虑对当前 Tab 所做的所有更改以及更改 Route 时。

这是我的根容器组件模板:

<section class="page">
    <ng-container *ngIf="adressenTabsList$ | async as tabsContainer">
        <form [formGroup]="adressenForm" *ngIf="tabsContainer.tabsList.length > 0 && !isLoading; else noResults">

            <mat-tab-group animationDuration="0ms" selectedTabChange)="onTabSelected($event)">
                
              <mat-tab *ngFor="let tab of tabsContainer.tabsList; trackBy: trackById; index as idx"
                    [label]="tab.label">
                    <span [ngSwitch]="tab.tabLabelKey">

                        <ng-container *ngSwitchCase="tabsType.kunde">
                            <kunde-tab [kundeForm]="kundeForm"></kunde-tab>
                            </ng-container>
                        </ng-container>

                        <ng-container *ngSwitchCase="tabsType.personen">
                           ...
                        </ng-container>
                     </span>
                  </mat-tab>
            </mat-tab-group>
        
  </section>

在组件中,我只是从后端获取选项卡对象:

  this.adressenTabsList$ = this.adressenService.getTabs()
      .pipe(
        map((list: TabList) => list)
      );

我希望能够为每个 Tab 内容获取具有 class mat-tab-body-content 的元素的 scrollTop 值(如下图所示)。

我尝试使用以下代码与可滚动选项卡内容进行交互,但我注意到没有注册的可滚动元素(即使 mat 选项卡内容应用了 cdkscrollable 指令(见上图) ,因此永远不会触发以下代码:

      this.scrollDispatcher.scrolled()
        .pipe(
        )
        .subscribe((cdk: CdkScrollable) => {
          this.zone.run(() => {
            const scrollPosition = cdk.getElementRef().nativeElement.scrollTop;
            console.log(scrollPosition);
          });
        });

我可以使用 _MatTabGroupBase 摘要 class 中的私有事件处理程序 _handleClick 来拦截 Tab 单击,然后相应地应用我们的逻辑来解决我的问题。

如果您有兴趣,我在 Dev.to article 中描述了该过程。

相关代码如下:

模板

<mat-tab-group mat-align-tabs="start">
  <mat-tab label="First">Content first tab</mat-tab>
  <mat-tab label="Second">Content second tab</mat-tab>
  <mat-tab label="Third">Content third tab</mat-tab>
</mat-tab-group>

组件class

import {
  AfterViewInit,
  ChangeDetectionStrategy,
  Component,
  ViewChild,
} from '@angular/core';
import { MatTabChangeEvent, MatTabGroup } from '@angular/material/tabs';
@Component({
  selector: 'app-component,
  templateUrl: './app-component.html',
  changeDetection: ChangeDetectionStrategy.OnPush,
})
export class AppComponent implements AfterViewInit {

// Get a reference of the MatTabGroup form the template
@ViewChild(MatTabGroup)
tabs?: MatTabGroup;

private currentTabIndex = 0;

ngAfterViewInit() {
  this.registerTabClick();
}

registerTabClick(): void {
  // Get the handler reference
  const handleTabClick = this.tabs._handleClick;

  this.tabs._handleClick = (tab, header, index) => {  
    // Get tab content reference for the current Tab -> currentTabIndex
    // since index here is already the "new" tab index
    const tabContent = this.getTabContentElement(this.currentTabIndex);

    const scrollPosition = Math.round(tabContent.scrollTop) || 0;

    this.store.dispatch(
       setTabScrollPosition({
         scrollPosition: scrollPosition,
         // ... other props
       })
    );

   // If you want to prevent the user to navigate to the new tab,
   // you can avoid invoking the 'apply' method below 
   handleTabClick.apply(this.tabs, [tab, header, index]);

   // We update the currentIndex, as we need it again when
   // another tab is clicked
   this.currentTabIndex = index;
  };
}

 // Returns the <mat-tab-body-content> with the tab content scroll 
 position given the target tab index
 private getTabContentElement(tabIndex: number) {
   return document.getElementsByClassName('mat-tab-body-content')[tabIndex];
 }
}