import {Injectable, PipeTransform} from '@angular/core'; import {BehaviorSubject, Observable, of, Subject} from 'rxjs'; import {DecimalPipe} from '@angular/common'; import {debounceTime, delay, switchMap, tap} from 'rxjs/operators'; import { Feature } from '../models/layer-type'; import { SortDirection } from 'src/app/directives/sortable-header.directive'; import { EnvService } from './env.service'; import { Structure } from '../models/structure'; interface SearchResult { features: Feature[]; total: number; } interface State { page: number; pageSize: number; searchTerm: string; sortColumn: string; sortDirection: SortDirection; searchField: string; } @Injectable({providedIn: 'root'}) export class TableViewDataService { private loading$ = new BehaviorSubject(true); private search$ = new Subject(); private features$ = new BehaviorSubject([]); private total$ = new BehaviorSubject(0); private features: Array = []; /** */ private structures$ = new BehaviorSubject([]); private structures: Array = []; /** */ private enumerations$ = new BehaviorSubject<{}>({}); private enumerations: {}; private state: State = { page: 1, pageSize: 10, searchTerm: '', sortColumn: '', sortDirection: '', searchField: '' }; constructor(private pipe: DecimalPipe, private envService: EnvService, ) { this.search$.pipe( tap(() => this.loading$.next(true)), debounceTime(200), switchMap(() => this._search()), delay(200), tap(() => this.loading$.next(false)) ).subscribe(result => { this.features$.next(result.features); this.total$.next(result.total); }); this.search$.next(); } getTotal$(): Observable { return this.total$.asObservable(); } getLoading$(): Observable { return this.loading$.asObservable(); } getEnumerations$(): Observable<{}> {return this.enumerations$.asObservable(); } getEnumerations(): {} { return this.enumerations; } getStructures(): Structure[] { return this.structures; } getStructures$(): Observable {return this.structures$.asObservable(); } getFeatures$(): Observable { return this.features$.asObservable(); } get page(): number { return this.state.page; } set page(page: number) { this._set({page}); } get pageSize(): number { return this.state.pageSize; } set pageSize(pageSize: number) { this._set({pageSize}); } get searchTerm(): string { return this.state.searchTerm; } set searchTerm(searchTerm: string) { this._set({searchTerm}); } set sortColumn(sortColumn: string) { this._set({sortColumn}); } set sortDirection(sortDirection: SortDirection) { this._set({sortDirection}); } setStructures(structures: Array): void { this.structures = structures; this.structures$.next(this.structures ); } setEnumerations(enumerations: {}): void {this.enumerations = enumerations; this.enumerations$.next(this.enumerations); } /** Fonctions */ private compare(v1, v2): number { return v1 < v2 ? -1 : v1 > v2 ? 1 : 0; } /** */ private sort(features: Feature[], column: string, direction: string): Feature[] { if (direction === '') { return features; } else { return [...features].sort((a, b) => { const res = this.compare(a.properties[column], b.properties[column]); return direction === 'asc' ? res : -res; }); } } /** */ private matches(feature: Feature, term: string, pipe: PipeTransform, searchField: string): boolean { let retour = false; const properties = feature.properties; if (!searchField) { Object.values(properties).forEach(propertie => { retour = retour || (propertie + '').toLowerCase().indexOf(term) > -1; }); } else if (properties[searchField] && ( (properties[searchField] + '').toLowerCase().indexOf(term) > -1) ) { retour = true; } return retour; } /** */ public searchFromUrlParam(): void { const locationUrl = new URL(window.location.href); let searchTerm = locationUrl.searchParams.get(this.envService.fitlerOnAllColumn); const searchField = locationUrl.searchParams.get(this.envService.filterColumnField); searchTerm = searchField ? locationUrl.searchParams.get(this.envService.filterColumnValue) : searchTerm; if (searchTerm) { this.state.searchTerm = searchTerm; } if (searchField) { this.state.searchField = searchField; } this.search$.next(); } /** */ public initFeatures(features: Array): void { this.features = features; this.search$.next(); } /** */ private _set(patch: Partial): void { Object.assign(this.state, patch); this.search$.next(); } /** */ private _search(): Observable { const {sortColumn, sortDirection, pageSize, page, searchTerm, searchField} = this.state; // 1. sort let features = this.sort(this.features, sortColumn, sortDirection); // 2. filter features = features.filter(feature => this.matches(feature, searchTerm, this.pipe, searchField )); const total = features.length; // 3. paginate features = features.slice((page - 1) * pageSize, (page - 1) * pageSize + pageSize); return of({features, total}); } }