【问题标题】:Material Angular scroll to element on mat-listMaterial Angular 滚动到 mat-list 上的元素
【发布时间】:2019-06-16 01:53:28
【问题描述】:

我有一个包含多个元素的 Angular 材质列表,可以选择其中一个。

当我加载此列表时,我想将列表向上滚动到该选定元素,以使其更加可见。有什么办法吗?

我正在考虑类似在 ngOnInit 中检查项目是否被选中,但我真的不知道如何将此列表滚动到该项目。

注意:不应该滚动整个页面,只滚动列表中的元素。

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>

组件.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);
    }

【问题讨论】:

  • 您能否发布您当前的代码,以便我们了解您正在使用什么?
  • 已简化发布,希望过程中不要出错。

标签: angular scroll angular-material material-design


【解决方案1】:

很难没有代码,但你可以给你的所有元素一个 ID,也许像

然后使用ngOnInit,您可以检查您选择的元素,使用document.getElementById('listelement'+elementid) 选择DOM 元素并将其滚动到视图中。 执行此操作的经典 JS 方法是 element.scrollIntoView();

【讨论】:

  • 我用简化的代码更新了问题。问题:它会滚动整个窗口,还是只滚动框内的元素?
  • 默认情况下它应该滚动整个窗口。您可以使用它来滚动容器:var target = document.getElementById("target"); target.parentNode.scrollTop = target.offsetTop; 在这种情况下,目标是您的元素
【解决方案2】:

查看此演示:https://stackblitz.com/edit/angular-yjbpoq?file=src%2Fapp%2Fhello.component.ts

要点是您将一个新指令附加到您的列表和每个列表项:

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

列表中的人可以为列表设置scrollTop(或使用scrollTo,或其他):

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

列表项上的可以报告他们的offsetTops:

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

通过@ViewChild@ViewChildren在组件中访问每种类型的指令:

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

AfterViewInit 生命周期钩子中,在项目列表中搜索与当前选定项目匹配的项目(此处为说明目的随机设置)。然后将列表的scrollTop 设置为该项目的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;
  }

【讨论】:

  • 谢谢,这看起来很不错,但我不知道为什么,当我尝试调用 this.list 时,我变得不确定。 this.list.scrollTo = this.listItems.find((_, i) => this.targets[i]["isSelected"] === "selected").offsetTo;我也尝试将#list 和#listItems 与选择器一起使用,但也没有任何变化。
  • 我想到了两件事:您是否创建了这两个指令,就像我在 Stackblitz 中所做的那样?您是否使用 AfterViewInit 而不是 OnInit(在 AfterViewInit 生命周期挂钩之前无法保证查看子项引用)?
  • 是的,我有两个指令,也用于 ngAfterViewInit 并在其他地方尝试过,我确信那里加载了数据;) listItems 也是空的:/
  • 那么我不太确定可能出了什么问题,没有直接看到你的代码:/
  • 好吧,愚蠢的我,好像我忘了'回合模块...无论如何,谢谢! ;)
【解决方案3】:

您可以将指令方法与 scrollIntoView 一起使用:

@Directive({
  selector: '[appScrollIntoView]'
})
export class ScrollIntoViewDirective implements OnChanges {
  @Input() appScrollIntoView: boolean | undefined;

  constructor(
    @Inject(PLATFORM_ID) private platformId: any,
    private elementRef: ElementRef
  ) {}

  ngOnChanges(simpleChange: SimpleChanges) {
    if (isPlatformBrowser(this.platformId)) {
      if (coerceBooleanProperty(this.appScrollIntoView)) {
        (this.elementRef.nativeElement as HTMLInputElement).scrollIntoView({
          behavior: 'smooth',
          block: 'center'
        });
      }
    }
  }
}

用法:

  <mat-list class="list" role="list">
    <mat-list-item
      class="list-item"
      [class.selected]="item === selectedItem"
      [appScrollIntoView]="item === selectedItem"
      *ngFor="let item of items"
      (click)="selectedItem = item"
      role="listitem"
      >{{ item.name }}</mat-list-item
    >
  </mat-list>

stackblitz

如果您使用材料选择列表,MatListOption 有一个 focus 方法,这是另一种选择。

@Directive({
  selector: 'mat-list-option[appScrollIntoView]'
})
export class ScrollIntoViewDirective implements OnChanges {
  @Input() appScrollIntoView: boolean | undefined;

  constructor(
    @Inject(PLATFORM_ID) private platformId: any,
    private elementRef: ElementRef,
    private matListOption: MatListOption
  ) {}

  ngOnChanges(simpleChange: SimpleChanges) {
    if (isPlatformBrowser(this.platformId)) {
      if (coerceBooleanProperty(this.appScrollIntoView)) {
        this.matListOption.focus();
      }
    }
  }
}

用法:

  <mat-selection-list
    (selectionChange)="selectedItem = $event.option.value"
    class="list"
    [multiple]="false"
    role="list"
    #itemList
  >
    <mat-list-option
      class="list-item"
      [appScrollIntoView]="option.selected"
      *ngFor="let item of items"
      role="listitem"
      [selected]="item === selectedItem"
      [value]="item"
      #option
      >{{ item.name }}</mat-list-option
    >
  </mat-selection-list>

stackblitz

【讨论】:

    猜你喜欢
    • 2019-03-05
    • 2018-08-18
    • 2020-07-02
    • 2017-05-26
    • 2019-05-15
    • 2019-04-04
    • 2019-11-06
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多