Angular 7 mat-autocomplete加载自动填充及动态更新数据问题
Hey there! Let's break down how to solve both of your requirements for the mat-autocomplete in Angular 7.
1. Auto-fill Data on Page Load
Right now your autocomplete only triggers when the user types at least 2 characters. To load default data when the page first loads, you need to adjust your observable stream to include an initial data fetch, and handle empty input states properly.
First, add a method to fetch default sales reps (e.g., the first 10 entries, or all if your dataset is small):
getDefaultSalesReps(): Observable<any[]> { const params = new QueryDto; params.$select = "UserId,FirstName,LastName,Username"; params.$top = 10; // Optional: limit to top 10 results return from(this.salesRepresentativesService.GetSalesRepresentatives(params)) .pipe( map(response => response.Data), catchError(() => of([])) // Return empty array on error instead of null ); }
Then update your salesPickerAutoComplete$ stream to merge the initial data fetch with the input change listener:
ngOnInit() { // Stream for initial page load data const initialData$ = this.getDefaultSalesReps(); // Stream for user input changes const searchData$ = this.autoCompleteControl.valueChanges.pipe( startWith(''), debounceTime(400), distinctUntilChanged(), switchMap(value => { if (value && value.length > 2) { return this.getSalesReps(value); } else if (value === '') { // Refresh default data when input is cleared return this.getDefaultSalesReps(); } else { return of([]); // Return empty array for inputs shorter than 3 chars } }) ); // Combine both streams: initial data first, then search results this.salesPickerAutoComplete$ = concat(initialData$, searchData$); // Optional: Auto-populate input with the first default entry initialData$.subscribe(data => { if (data.length > 0) { this.autoCompleteControl.setValue(data[0]); } }); }
2. Dynamically Push/Modify Autocomplete Options
To dynamically update the autocomplete options at any time (e.g., after adding a new sales rep, or refreshing data from another action), use a BehaviorSubject to control the data stream. This lets you manually push new data to the observable whenever you need.
First, define a BehaviorSubject in your component:
import { BehaviorSubject } from 'rxjs'; // ... private salesRepsSubject = new BehaviorSubject<any[]>([]); public salesPickerAutoComplete$ = this.salesRepsSubject.asObservable();
Then update your initialization logic to push data to the subject:
ngOnInit() { // Load initial data on page load this.getDefaultSalesReps().subscribe(data => { this.salesRepsSubject.next(data); }); // Handle input changes this.autoCompleteControl.valueChanges.pipe( startWith(''), debounceTime(400), distinctUntilChanged() ).subscribe(value => { if (value && value.length > 2) { this.getSalesReps(value).subscribe(data => { this.salesRepsSubject.next(data); }); } else if (value === '') { this.getDefaultSalesReps().subscribe(data => { this.salesRepsSubject.next(data); }); } else { this.salesRepsSubject.next([]); } }); }
Now, whenever you need to update the options (e.g., after adding a new sales rep), just call next() on the subject with the updated array:
// Example: Add a new sales rep to the autocomplete list addNewSalesRep(newRep: any) { const currentReps = this.salesRepsSubject.getValue(); // Create a new array to trigger change detection const updatedReps = [...currentReps, newRep]; this.salesRepsSubject.next(updatedReps); } // Example: Refresh the entire list manually refreshSalesReps() { this.getDefaultSalesReps().subscribe(data => { this.salesRepsSubject.next(data); }); }
A quick safety note: In your original getSalesReps method, single quotes in user input can break your OData filter. Add a quick escape to handle this:
getSalesReps(value: string): Observable<any[]> { const escapedValue = value.replace(/'/g, "''"); // Escape single quotes to avoid filter errors const params = new QueryDto; params.$filter = `substringof('${escapedValue}',FirstName) or substringof('${escapedValue}',LastName)`; params.$select = "UserId,FirstName,LastName,Username"; return from(this.salesRepresentativesService.GetSalesRepresentatives(params)) .pipe( map(response => response.Data), catchError(() => of([])) ); }
Your template code doesn't need any changes—it will automatically update whenever the salesPickerAutoComplete$ observable emits new data.
内容的提问来源于stack exchange,提问作者Ankur

