I try to do a custom autocomplete component in my angular 16 project. That component should be able to lazy fetch the data from my component store.
I achieve to do something like that :
// MySearchComponent
@Input({ required: true }) searchValues$?: Observable<SearchInputObject[]>;
@Input({ required: true }) searchValuesSubscription!: any;// the trigger
@Input({ required: true }) fieldCtrl!: FormControl<SearchInputObject | null>; // form control to return the selected value
With searchValuesSubscription a trigger of my effect, and searchValues$ the select associated in the store.
I find that solution not very developper friendly, as if I use the component 10 times, I'll have to create those 2 properties in the parent component to use MySearchComponent
ngOnInit() {
this.searchValues$ = this._myStore.companyCategories$;
this.searchValuesSubscription$ = this._myStore.loadCompanyCategories;
}
And to call the component :
<app-async-search-input
[fieldCtrl]="docCategoryCtrl"
[searchValues$]="searchValues$"
[searchValuesSubscription]="searchValuesSubscription$">
</app-async-search-input>
An extract of the Effect, just in case it helps :)
readonly loadCompanyCategories = this.effect<void>(trigger$ =>
trigger$.pipe(
exhaustMap(() =>
combineLatest([this.userInstance$, this.selectedCompanyId$]).pipe(
switchMap(([instance, selectedCompanyId]) => {
if (instance && selectedCompanyId) {
return this._apiService.getCompanyCategories(instance, selectedCompanyId).pipe(
tapResponse(
companiesCategories => {
this.patchState({
nonPersistent: {
...this.get().nonPersistent,
companyCategories: companiesCategories,
},
});
},
(error: HttpErrorResponse) => console.error(error)
)
);
}
return [];
})
)
)
)
);
Here a stackblitz that show a simplified version of what I did : https://stackblitz.com/edit/stackblitz-starters-frdx5b?file=src%2Fapp%2Fasync-autocomplete%2Fasync-autocomplete.component.ts You need to click on "Load books" so the AsyncAutocompleteComponent trigger the store, and then display the list of book in a select.
Is there a way to have a trigger that return on observable of the values ? Or is it something that shouldn't be done with an Effect ? Is there another way to do that ? I'm open to all advice ! Thanks !
Ok, here is a forked stackblitz that shows the correct way to handle event and data flow.
As I mentioned in my comment, passing observables and subscriptions into components is an anti-pattern. Doing this should be avoided unless absolutely necessary.
I am going to just post the pieces of code that are different than what you show above...
When you need to trigger something in a parent component, use an
@Output, rather than passing a subscription.Then this component should handle the async binding for the data, so that change detection is run for the async-autocomplete component whenever the data changes. Also, the there is a new event handler for when the button is pressed, which calls the load in the store.
The load function effect you wrote also needed some adjustments. There really is no need to use an
exhaustMapand then aswitchMap. I recommend using eithercombineLastestWithfromrxjsor theconcatLatestFromfound in the@ngrx/operatorslibrary when you need to unwrap observables while processing an effect.Please see the stackblitz to see how it all works together.