I've been looking throu the ngrx example-app and trying to expand it with the following:
- when searching for the book the query is written in the url as query parameters like so http://localhost:4200/book/find?query=book_title
- when opening the page with an url that containes a query parameter the search input box should be filled and the search displayed
What would be the right way to do such a thing ?
I have added my solution, it does the job but i'm not sure if it is any good. In it i am storing the query parameter in two places which seem like an antipattern
My solution:
in the app/books/effects/books.ts added a router navigation in the search effect, so that whenever a search is triggered the url will be updated with the query string
search$: Observable<Action> = this.actions$.pipe(
ofType<Search>(BookActionTypes.Search),
debounceTime(this.debounce || 300, this.scheduler || async),
map(action => action.payload),
switchMap(query => {
if (query === '') {
return empty();
}
const nextSearch$ = this.actions$.pipe(
ofType(BookActionTypes.Search),
skip(1)
);
return this.googleBooks.searchBooks(query).pipe(
takeUntil(nextSearch$),
map((books: Book[]) => {
// added //
this.router.navigate([], {
queryParams: { query: query },
queryParamsHandling: 'merge',
});
///////////
return new SearchComplete(books);
}),
catchError(err => of(new SearchError(err)))
);
})
);
in app/reducers/index.ts added the selector for router query parameters:
export const getRouterState = createFeatureSelector<
fromRouter.RouterReducerState<RouterStateUrl>
>('router');
export const getQueryParams = createSelector(
getRouterState,
state => state.state.queryParams
);
in app/books/containers/find-book-page.ts added the method
urlQuery() {
// dispatch a search action if there is a query string in url
this.store
.pipe(select(fromRoot.getQueryParams), take(1))
.subscribe(queryParams => {
if (queryParams.query) {
this.search(queryParams.query);
}
});
}
this method is being called from the constructor in the find-book-page component, so when ever this component is loaded it will check if there is a query string in the url and if so dispatch an action with that query string
constructor(private store: Store<fromBooks.State>) {
this.searchQuery$ = store.pipe(select(fromBooks.getSearchQuery), take(1));
this.books$ = store.pipe(select(fromBooks.getSearchResults));
this.loading$ = store.pipe(select(fromBooks.getSearchLoading));
this.error$ = store.pipe(select(fromBooks.getSearchError));
this.urlQuery();
}