#javascript #angular #angular-material #mat-autocomplete
Вопрос:
Пожалуйста, не помечайте его как дубликат
Я новичок в дизайне угловых материалов, и у меня проблема с автозаполнением мата. У меня есть несколько Mat-Autocomplete
в формаре группы FromGroup. keyup
В поле ввода он получает данные из вызовов API и заполняет автозаполнение. После получения данных по ключу откроется панель.
- когда я нажимаю word, открывается список автозаполнения, тогда я хочу, чтобы этот список был бесконечным-прокрутка
- У меня есть несколько автозаполнений в 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$ запускается повторно и выдает нам соответствующий вывод.