Refactored the clear input field handling for search to new observable

This commit is contained in:
Joseph Milazzo 2022-11-10 07:00:32 -06:00
parent 7263a561d9
commit a9ca770307
4 changed files with 36 additions and 33 deletions

View file

@ -1,6 +1,5 @@
import { HttpClient } from '@angular/common/http'; import { HttpClient } from '@angular/common/http';
import { Injectable } from '@angular/core'; import { Injectable } from '@angular/core';
import { Subject } from '@microsoft/signalr';
import { distinctUntilChanged, filter, map, Observable, of, ReplaySubject, startWith, switchMap } from 'rxjs'; import { distinctUntilChanged, filter, map, Observable, of, ReplaySubject, startWith, switchMap } from 'rxjs';
import { environment } from 'src/environments/environment'; import { environment } from 'src/environments/environment';
import { SearchResultGroup } from '../_models/search/search-result-group'; import { SearchResultGroup } from '../_models/search/search-result-group';
@ -13,27 +12,23 @@ export class SearchService {
baseUrl = environment.apiUrl; baseUrl = environment.apiUrl;
private searchSubject: ReplaySubject<string> = new ReplaySubject(); private searchSubject: ReplaySubject<string> = new ReplaySubject(1);
searchResults$: Observable<SearchResultGroup>; searchResults$: Observable<SearchResultGroup>;
constructor(private httpClient: HttpClient) { constructor(private httpClient: HttpClient) {
this.searchResults$ = this.searchSubject.pipe( this.searchResults$ = this.searchSubject.pipe(
startWith(''), startWith(''),
map(val => val.trim()), map(val => val.trim()),
filter(term => term !== '' && term !== null && term !== undefined),
distinctUntilChanged(), distinctUntilChanged(),
switchMap(term => { switchMap(term => {
return this.httpClient.get<SearchResultGroup>(this.baseUrl + 'search/search?queryString=' + encodeURIComponent(term)); if (term === '' || term === null || term === undefined) return of(new SearchResultGroup());
return this.httpClient.get<SearchResultGroup>(this.baseUrl + 'search/search?queryString=' + encodeURIComponent(term))
}) })
); );
} }
search(term: string) { search(term: string) {
this.searchSubject.next(term); this.searchSubject.next(term);
if (term === '') {
return of(new SearchResultGroup());
}
return this.httpClient.get<SearchResultGroup>(this.baseUrl + 'search/search?queryString=' + encodeURIComponent(term));
} }
getSeriesForMangaFile(mangaFileId: number) { getSeriesForMangaFile(mangaFileId: number) {

View file

@ -129,11 +129,14 @@ export class EditSeriesRelationComponent implements OnInit, OnDestroy {
seriesSettings.id = 'relation--' + index; seriesSettings.id = 'relation--' + index;
seriesSettings.unique = true; seriesSettings.unique = true;
seriesSettings.addIfNonExisting = false; seriesSettings.addIfNonExisting = false;
seriesSettings.fetchFn = (searchFilter: string) => this.searchService.search(searchFilter).pipe( seriesSettings.fetchFn = (searchFilter: string) => {
map(group => group.series), this.searchService.search(searchFilter);
map(items => seriesSettings.compareFn(items, searchFilter)), return this.searchService.searchResults$.pipe(
map(series => series.filter(s => s.seriesId !== this.series.id)), map(group => group.series),
); map(items => seriesSettings.compareFn(items, searchFilter)),
map(series => series.filter(s => s.seriesId !== this.series.id)),
);
}
seriesSettings.compareFn = (options: SearchResult[], filter: string) => { seriesSettings.compareFn = (options: SearchResult[], filter: string) => {
return options.filter(m => this.utilityService.filter(m.name, filter)); return options.filter(m => this.utilityService.filter(m.name, filter));
@ -144,7 +147,8 @@ export class EditSeriesRelationComponent implements OnInit, OnDestroy {
} }
if (series !== undefined) { if (series !== undefined) {
return this.searchService.search(series.name).pipe( this.searchService.search(series.name)
return this.searchService.searchResults$.pipe(
map(group => group.series), map(results => { map(group => group.series), map(results => {
seriesSettings.savedData = results.filter(s => s.seriesId === series.id); seriesSettings.savedData = results.filter(s => s.seriesId === series.id);
return seriesSettings; return seriesSettings;

View file

@ -192,7 +192,7 @@ NZ0ZV4zm4/L1dfnYNCrjTFq9G03rmj5D+Y4i0OHuL3GFPJytaM54AAAAAElFTkSuQmCC
[minQueryLength]="2" [minQueryLength]="2"
initialValue="" initialValue=""
placeholder="Search…" placeholder="Search…"
[grouppedData]="(searchService.searchResults$ | async) || searchResults" [grouppedData]="(search$ | async) || emptyResult"
(inputChanged)="onChangeSearch($event)" (inputChanged)="onChangeSearch($event)"
(clearField)="clearSearch()" (clearField)="clearSearch()"
(focusChanged)="focusUpdate($event)" (focusChanged)="focusUpdate($event)"

View file

@ -1,8 +1,8 @@
import { DOCUMENT } from '@angular/common'; import { DOCUMENT } from '@angular/common';
import { ChangeDetectionStrategy, ChangeDetectorRef, Component, ElementRef, Inject, OnDestroy, OnInit, ViewChild } from '@angular/core'; import { ChangeDetectionStrategy, ChangeDetectorRef, Component, ElementRef, Inject, OnDestroy, OnInit, ViewChild } from '@angular/core';
import { NavigationEnd, Router } from '@angular/router'; import { NavigationEnd, Router } from '@angular/router';
import { fromEvent, Subject } from 'rxjs'; import { fromEvent, Observable, of, Subject } from 'rxjs';
import { debounceTime, distinctUntilChanged, filter, takeUntil, tap } from 'rxjs/operators'; import { catchError, debounceTime, distinctUntilChanged, filter, startWith, takeUntil, tap } from 'rxjs/operators';
import { Chapter } from 'src/app/_models/chapter'; import { Chapter } from 'src/app/_models/chapter';
import { MangaFile } from 'src/app/_models/manga-file'; import { MangaFile } from 'src/app/_models/manga-file';
import { ScrollService } from 'src/app/_services/scroll.service'; import { ScrollService } from 'src/app/_services/scroll.service';
@ -31,7 +31,8 @@ export class NavHeaderComponent implements OnInit, OnDestroy {
isLoading = false; isLoading = false;
debounceTime = 300; debounceTime = 300;
imageStyles = {width: '24px', 'margin-top': '5px'}; imageStyles = {width: '24px', 'margin-top': '5px'};
searchResults: SearchResultGroup = new SearchResultGroup(); emptyResult: SearchResultGroup = new SearchResultGroup();
search$!: Observable<SearchResultGroup>;
searchTerm = ''; searchTerm = '';
customFilter: (items: SearchResult[], query: string) => SearchResult[] = (items: SearchResult[], query: string) => { customFilter: (items: SearchResult[], query: string) => SearchResult[] = (items: SearchResult[], query: string) => {
const normalizedQuery = query.trim().toLowerCase(); const normalizedQuery = query.trim().toLowerCase();
@ -54,6 +55,21 @@ export class NavHeaderComponent implements OnInit, OnDestroy {
public imageService: ImageService, @Inject(DOCUMENT) private document: Document, public imageService: ImageService, @Inject(DOCUMENT) private document: Document,
private scrollService: ScrollService, public searchService: SearchService, private readonly cdRef: ChangeDetectorRef) { private scrollService: ScrollService, public searchService: SearchService, private readonly cdRef: ChangeDetectorRef) {
this.scrollElem = this.document.body; this.scrollElem = this.document.body;
this.search$ = this.searchService.searchResults$.pipe(
startWith(new SearchResultGroup()),
takeUntil(this.onDestroy),
tap((_) => {
this.isLoading = false;
this.cdRef.markForCheck();
}),
catchError(() => {
this.isLoading = false;
this.searchTerm = '';
this.cdRef.markForCheck();
return of();
})
);
} }
ngOnInit(): void { ngOnInit(): void {
@ -107,20 +123,8 @@ export class NavHeaderComponent implements OnInit, OnDestroy {
onChangeSearch(val: string) { onChangeSearch(val: string) {
this.isLoading = true; this.isLoading = true;
this.searchTerm = val.trim(); this.searchTerm = val.trim();
this.cdRef.markForCheck(); this.searchService.search(val);
this.cdRef.markForCheck();
this.searchService.search(val.trim());
this.searchService.search(val.trim()).pipe(takeUntil(this.onDestroy)).subscribe((results: SearchResultGroup) => {
//this.searchResults = results;
this.isLoading = false;
this.cdRef.markForCheck();
}, err => {
this.searchResults.reset();
this.isLoading = false;
this.searchTerm = '';
this.cdRef.markForCheck();
});
} }
goTo(queryParamName: string, filter: any) { goTo(queryParamName: string, filter: any) {
@ -173,7 +177,7 @@ export class NavHeaderComponent implements OnInit, OnDestroy {
clearSearch() { clearSearch() {
this.searchViewRef.clear(); this.searchViewRef.clear();
this.searchTerm = ''; this.searchTerm = '';
this.searchResults = new SearchResultGroup(); this.searchService.search('');
this.cdRef.markForCheck(); this.cdRef.markForCheck();
} }