import { Resource } from '@ngxp/rest'; import { isEmpty } from 'lodash-es'; import { BehaviorSubject, Observable, first, map, tap, withLatestFrom } from 'rxjs'; import { EMPTY_STRING, isNotEmpty } from '../tech.util'; import { LinkRelationName, ListItemResource, SearchResourceServiceConfig } from './resource.model'; import { ResourceRepository } from './resource.repository'; import { ListResource, StateResource, createEmptyStateResource, createStateResource, } from './resource.util'; /** * B = Type of baseresource * T = Type of listresource * I = Type of items in listresource */ export class ResourceSearchService< B extends Resource, T extends ListResource, I extends ListItemResource, > { readonly listResource: BehaviorSubject<StateResource<T>> = new BehaviorSubject( createEmptyStateResource(), ); readonly searchBy: BehaviorSubject<string> = new BehaviorSubject<string>(EMPTY_STRING); readonly selectedResource: BehaviorSubject<I> = new BehaviorSubject<I>(null); constructor( private config: SearchResourceServiceConfig<B>, private repository: ResourceRepository, ) {} public getResultList(): Observable<StateResource<T>> { return this.selectListResource().pipe( withLatestFrom(this.selectSearchBy(), this.config.baseResource), tap(([listResource, searchBy, baseResource]) => { this.handleChanges(listResource, searchBy, baseResource); }), map(([listResource]) => listResource), ); } private selectListResource(): Observable<StateResource<T>> { return this.listResource.asObservable(); } private selectSearchBy(): Observable<string> { return this.searchBy.asObservable(); } handleChanges(listResource: StateResource<T>, searchBy: string, baseResource: B): void { if (listResource.loading) this.doSearch(searchBy, baseResource); } doSearch(searchBy: string, baseResource: B): void { if (isNotEmpty(searchBy)) { this.dispatchSearch(baseResource, this.config.searchLinkRel, searchBy); } if (isEmpty(searchBy)) { this.dispatchClearSearchList(); } } dispatchSearch(baseResource: B, linkRel: LinkRelationName, searchBy: string): void { this.repository .search<T>(baseResource, linkRel, searchBy) .pipe(first()) .subscribe((result: T) => this.dispatchSearchSuccess(result)); } private dispatchSearchSuccess(result: T): void { this.listResource.next(createStateResource(result)); } public clearResultList(): void { this.dispatchClearSearchList(); } private dispatchClearSearchList(): void { this.listResource.next(createEmptyStateResource()); } public search(searchBy: string): void { this.dispatchInitSearch(searchBy); } private dispatchInitSearch(searchBy: string): void { this.searchBy.next(searchBy); this.listResource.next({ ...this.listResource.value, loading: true }); } public getSelectedResult(): Observable<I> { return this.selectedResource.asObservable(); } public selectResult(selected: I): void { this.selectedResource.next(selected); } public clearSelectedResult(): void { this.selectedResource.next(null); } }