0

请不要将其标记为重复

我是角度材料设计的新手,但我对 mat-autocomplete 有疑问。我Mat-Autocomplete在 FromGroup 的 FormArray 中有多个。在keyup输入字段中,它从 API 调用中获取数据并填充自动完成。在 Keyup 上获取数据后,它将打开面板。

  1. 当我按单词然后自动完成列表打开然后我希望这个列表无限滚动
  2. 我在 formGroup 的 formArray 中有多个自动完成功能。

我不想在项目中使用第三方依赖项,例如 ngx-infinite-scroll。

在此处输入图像描述

4

1 回答 1

0

此Stackblitz 链接中的工作演示

当您想检测自动完成滚动结束位置时,您可以使用自定义指令。在该指令中,您可以计算面板控件的位置并检测滚动结束位置,一旦检测到滚动结束,您就可以向组件发出事件。指令名称是mat-autocomplete[optionsScroll]为了自动检测带有 optionScroll 事件的 mat-autocomplete 组件,并且此自定义指令应用于所有此匹配组件。指令如下..

export interface IAutoCompleteScrollEvent {
  autoComplete: MatAutocomplete;
  scrollEvent: Event;
}

@Directive({
  selector: 'mat-autocomplete[optionsScroll]',
  exportAs: 'mat-autocomplete[optionsScroll]'
})
export class MatAutocompleteOptionsScrollDirective {
   @Input() thresholdPercent = 0.8;
   @Output('optionsScroll') scroll = new EventEmitter<IAutoCompleteScrollEvent>();
  _onDestroy = new Subject();
  constructor(public autoComplete: MatAutocomplete) {
     this.autoComplete.opened
    .pipe(
      tap(() => {
      // Note: When autocomplete raises opened, panel is not yet created (by Overlay)
      // Note: The panel will be available on next tick
      // Note: The panel wil NOT open if there are no options to display
      setTimeout(() => {
        // Note: remove listner just for safety, in case the close event is skipped.
        this.removeScrollEventListener();
        this.autoComplete.panel.nativeElement.addEventListener(
          'scroll',
          this.onScroll.bind(this)
        );
      }, 5000);
    }),
    takeUntil(this._onDestroy)
  )
  .subscribe();

this.autoComplete.closed
  .pipe(
    tap(() => this.removeScrollEventListener()),
    takeUntil(this._onDestroy)
  )
  .subscribe();
}

 private removeScrollEventListener() {
  if (this.autoComplete?.panel) {
   this.autoComplete.panel.nativeElement.removeEventListener(
    'scroll',
    this.onScroll
   );
 }
}

 ngOnDestroy() {
   this._onDestroy.next();
   this._onDestroy.complete();

   this.removeScrollEventListener();
 }

 onScroll(event: Event) {
   if (this.thresholdPercent === undefined) {
     console.log('undefined');
     this.scroll.next({ autoComplete: this.autoComplete, scrollEvent: event });
   } else {
     const scrollTop = (event.target as HTMLElement).scrollTop;
     const scrollHeight = (event.target as HTMLElement).scrollHeight;
     const elementHeight = (event.target as HTMLElement).clientHeight;
     const atBottom = scrollHeight === scrollTop + elementHeight;
   if (atBottom) {
      this.scroll.next();
   }
  }
 }
}

现在,您必须调用滚动事件来实现 mat-autocomplete。在每次滚动结束时,我们的指令都会调用 onScroll() 事件。

<mat-autocomplete (optionsScroll)="onScroll()" > ... </mat-autocomplete>

现在,您必须像这样将第一个和下一个数据块加载到 mat-autocomplete 中。

  weightData$ = this.startSearch$.pipe(
      startWith(''),
      debounceTime(200),
      switchMap(filter => {
         //Note: Reset the page with every new seach text
         let currentPage = 1;
         return this.next$.pipe(
            startWith(currentPage),
              //Note: Until the backend responds, ignore NextPage requests.
            exhaustMap(_ => this.getProducts(String(filter), currentPage)),
            tap(() => currentPage++),
              //Note: This is a custom operator because we also need the last emitted value.
             //Note: Stop if there are no more pages, or no results at all for the current search text.
            takeWhileInclusive((p: any) => p.length > 0),
            scan((allProducts: any, newProducts: any) => allProducts.concat(newProducts), [] ) );
            })
          );
   
  private getProducts(startsWith: string, page: number): Observable<any[]> {
  
   const take = 6;
   const skip = page > 0 ? (page - 1) * take : 0;

   const filtered = this.weightData.filter(option => String(option).toLowerCase().startsWith(startsWith.toLowerCase()));

   return of(filtered.slice(skip, skip + take));
  }
  onScroll() {
     this.next$.next();
  }

所以第一次我们只加载第一块数据,当我们到达滚动结束时,我们再次使用 next$ 主题发出事件,我们的流 weightData$ 重新运行并给我们适当的输出。

于 2021-06-11T13:21:36.580 回答