admin管理员组文章数量:1221303
I'm trying to implement infinite scroll for autocomplete in angular material 6. My scenario is straightforward, I have an input filed with autocomplete enabled. when the user types, I will make the HTTP call with text in the input filed to show the results as suggestions. But I want to show only 25 suggestions, if results count more than 25 when user scroll to the bottom I want to add one more 25. Like this in angular 2
I was not able to find online.
Please advise or help me. Thankyou in advance.
<mat-form-field>
<input matInput placeholder="Experiment Name" formControlName="experimentName" [matAutocomplete]="expNamesAutocomplete">
</mat-form-field>
<mat-autocomplete #expNamesAutocomplete="matAutocomplete">
<mat-option *ngFor="let option of suggestedExpNames" [value]="option">
{{ option }}
</mat-option>
</mat-autocomplete>
I'm trying to implement infinite scroll for autocomplete in angular material 6. My scenario is straightforward, I have an input filed with autocomplete enabled. when the user types, I will make the HTTP call with text in the input filed to show the results as suggestions. But I want to show only 25 suggestions, if results count more than 25 when user scroll to the bottom I want to add one more 25. Like this in angular 2
I was not able to find online.
Please advise or help me. Thankyou in advance.
<mat-form-field>
<input matInput placeholder="Experiment Name" formControlName="experimentName" [matAutocomplete]="expNamesAutocomplete">
</mat-form-field>
<mat-autocomplete #expNamesAutocomplete="matAutocomplete">
<mat-option *ngFor="let option of suggestedExpNames" [value]="option">
{{ option }}
</mat-option>
</mat-autocomplete>
Share
Improve this question
asked Jul 11, 2018 at 20:47
KrishnaKrishna
6603 gold badges14 silver badges31 bronze badges
1 Answer
Reset to default 19I know the post is old, but i'm leaving the solution here in case anybody needs it.
The trick is to get the reference to the mat-autocomplete panel's scrollbar. I've done this using a custom directive:
import { Directive, ElementRef, EventEmitter, Input, Output, Host, Self, Optional, AfterViewInit, OnDestroy } from '@angular/core';
import { MatAutocomplete } from '@angular/material';
import { Observable, fromEvent, of, Subject, merge, combineLatest } from 'rxjs';
import { map, startWith, switchMap, tap, debounceTime, filter, scan, withLatestFrom, mergeMap, takeUntil, takeWhile, distinctUntilChanged, skipUntil, exhaustMap, endWith } from 'rxjs/operators';
import { takeWhileInclusive } from 'rxjs-take-while-inclusive';
export interface IAutoCompleteScrollEvent {
autoComplete: MatAutocomplete;
scrollEvent: Event;
}
@Directive({
selector: 'mat-autocomplete[optionsScroll]'
})
export class OptionsScrollDirective implements OnDestroy {
@Input() thresholdPercent = .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))
});
}),
takeUntil(this._onDestroy)).subscribe();
this.autoComplete.closed.pipe(
tap(() => this.removeScrollEventListener()),
takeUntil(this._onDestroy)).subscribe();
}
private removeScrollEventListener() {
this.autoComplete.panel.nativeElement
.removeEventListener('scroll', this.onScroll);
}
ngOnDestroy() {
this._onDestroy.next();
this._onDestroy.complete();
this.removeScrollEventListener();
}
onScroll(event: Event) {
if (this.thresholdPercent === undefined) {
this.scroll.next({ autoComplete: this.autoComplete, scrollEvent: event });
} else {
const threshold = this.thresholdPercent * 100 * event.target.scrollHeight / 100;
const current = event.target.scrollTop + event.target.clientHeight;
//console.log(`scroll ${current}, threshold: ${threshold}`)
if (current > threshold) {
//console.log('load next page');
this.scroll.next({ autoComplete: this.autoComplete, scrollEvent: event });
}
}
}
}
After this what remains is to load more data from the server when the scrollbar reaches 80% threshold:
import { Component, OnInit } from '@angular/core';
import { FormControl } from '@angular/forms';
import { Observable, fromEvent, of, Subject, merge, combineLatest } from 'rxjs';
import { map, startWith, switchMap, tap, debounceTime, filter, scan, withLatestFrom, mergeMap, takeUntil, takeWhile, distinctUntilChanged, skipUntil, exhaustMap, endWith } from 'rxjs/operators';
import { MatAutocomplete } from '@angular/material/autocomplete';
import { takeWhileInclusive } from 'rxjs-take-while-inclusive';
export interface ILookup {
id: number,
name: string
}
@Component({
selector: 'autocomplete-filter-example',
templateUrl: 'autocomplete-filter-example.html',
styleUrls: ['autocomplete-filter-example.scss'],
})
export class AutocompleteFilterExample implements OnInit {
searchText = new FormControl({ id: 2, name: 'ana' });
filteredLookups$: Observable<ILookup[]>;
private lookups: ILookup[] = [];
private nextPage$ = new Subject();
private _onDestroy = new Subject();
// Fake backend api
private getProducts(startsWith: string, page: number): Observable<ILookup[]> {
console.log(`api call filter: ${startsWith}`);
const take = 10;
const skip = page > 0 ? (page - 1) * take : 0;
const filtered = this.lookups
.filter(option => option.name.toLowerCase().startsWith(startsWith.toLowerCase()))
console.log(`skip: ${skip}, take: ${take}`);
return of(filtered.slice(skip, skip + take));
}
ngOnInit() {
// Note: Generate some mock data
this.lookups = [{ id: 1994, name: 'ana' }, { id: 1989, name: 'narcis' }]
for (let i = 1; i < 100; i++) {
this.lookups.push({ id: i, name: 'test' + i })
}
// Note: listen for search text changes
const filter$ = this.searchText.valueChanges.pipe(
startWith(''),
debounceTime(200),
// Note: If the option valye is bound to object, after selecting the option
// Note: the value will change from string to {}. We want to perform search
// Note: only when the type is string (no match)
filter(q => typeof q === "string"));
// Note: There are 2 stream here: the search text changes stream and the nextPage$ (raised by directive at 80% scroll)
// Note: On every search text change, we issue a backend request starting the first page
// Note: While the backend is processing our request we ignore any other NextPage emitts (exhaustMap).
// Note: If in this time the search text changes, we don't need those results anymore (switchMap)
this.filteredLookups$ = filter$.pipe(
switchMap(filter => {
//Note: Reset the page with every new seach text
let currentPage = 1;
return this.nextPage$.pipe(
startWith(currentPage),
//Note: Until the backend responds, ignore NextPage requests.
exhaustMap(_ => this.getProducts(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 => p.length > 0),
scan((allProducts, newProducts) => allProducts.concat(newProducts), []),
);
})); // Note: We let asyncPipe subscribe.
}
displayWith(lookup) {
return lookup ? lookup.name : null;
}
onScroll() {
//Note: This is called multiple times after the scroll has reached the 80% threshold position.
this.nextPage$.next();
}
ngOnDestroy() {
this._onDestroy.next();
this._onDestroy.complete();
}
}
Note: I'm using a custom rxjs operator rxjs-take-while-inclusive.
You case see it in action here: DEMO
本文标签: javascriptInfinite scroll for autocomplete in Angular Material 6Stack Overflow
版权声明:本文标题:javascript - Infinite scroll for autocomplete in Angular Material 6 - Stack Overflow 内容由网友自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:http://www.betaflare.com/web/1739344157a2159077.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。
发表评论