Бесконечная прокрутка в мате-угол автозаполнения 11

#javascript #angular #angular-material #mat-autocomplete

Вопрос:

Пожалуйста, не помечайте его как дубликат

Я новичок в дизайне угловых материалов, и у меня проблема с автозаполнением мата. У меня есть несколько Mat-Autocomplete в формаре группы FromGroup. keyup В поле ввода он получает данные из вызовов API и заполняет автозаполнение. После получения данных по ключу откроется панель.

  1. когда я нажимаю word, открывается список автозаполнения, тогда я хочу, чтобы этот список был бесконечным-прокрутка
  2. У меня есть несколько автозаполнений в FormArray FormGroup.

Я не хочу использовать сторонние зависимости в проекте, такие как ngx-infinite-scroll.

введите описание изображения здесь

Комментарии:

1. когда вы нажимаете word, открывается список автозаполнения, тогда вы хотите, чтобы этот список был бесконечным-прокрутка ?

2. да. @GaurangDhorda

3. Данные поступают из api ? если да, то ваш серверный api поддерживает разбиение на страницы для загрузки дополнительных данных с сервера для автозаполнения ?

4. stackblitz.com/edit/… вот ваш пример

5. stackblitz.com/edit/… зацени это @neilnikkunilesh

Ответ №1:

Рабочая демонстрация по этой ссылке на Stackblitz

Если вы хотите определить конечную позицию прокрутки автозаполнения, вы можете использовать пользовательскую директиву. В этой директиве вы можете рассчитать положение элемента управления панелью и определить конечное положение прокрутки, а после обнаружения конца прокрутки вы можете отправить событие компоненту. Имя директивы mat-autocomplete[optionsScroll] таково, что она автоматически обнаруживает компонент автозаполнения с помощью события optionScroll, и эта пользовательская директива применяется ко всему этому соответствующему компоненту. Директива заключается в следующем..

 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();
   }
  }
 }
}
 

Теперь вам нужно вызвать событие прокрутки для автозаполнения мата. На каждом конце прокрутки событие onScroll() вызывается нашей директивой.

 <mat-autocomplete (optionsScroll)="onScroll()" > ... </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();
  }
 

Поэтому в первый раз мы загружаем только первый фрагмент данных, когда мы достигли конца прокрутки, затем мы снова выдаем событие, используя следующий$ subject, и наш поток weightData$ запускается повторно и выдает нам соответствующий вывод.