import { DecimalPipe } from '@angular/common' ; import { Injectable , PipeTransform } from '@angular/core' ; import { COUNTRIES } from '@modules/tables/data/countries' ; import { SortDirection } from '@modules/tables/directives' ; import { Country } from '@modules/tables/models' ; import { BehaviorSubject , Observable , of , Subject } from 'rxjs' ; import { debounceTime , delay , switchMap , tap } from 'rxjs/operators' ; interface SearchResult { countries : Country [ ] ; total : number ; } interface State { page : number ; pageSize : number ; searchTerm : string ; sortColumn : string ; sortDirection : SortDirection ; } function compare ( v1 , v2 ) { return v1 < v2 ? - 1 : v1 > v2 ? 1 : 0 ; } function sort ( countries , column , direction ) { if ( direction === '' ) { return countries ; } else { return [ ... countries ] . sort ( ( a , b ) => { const res = compare ( a [ column ] , b [ column ] ) ; return direction === 'asc' ? res : - res ; } ) ; } } function matches ( country , term , pipe ) { return ( country . name . toLowerCase ( ) . includes ( term . toLowerCase ( ) ) || pipe . transform ( country . area ) . includes ( term ) || pipe . transform ( country . population ) . includes ( term ) ) ; } @ Injectable ( { providedIn : 'root' } ) export class CountryService { private _loading$ = new BehaviorSubject < boolean > ( true ) ; private _search$ = new Subject < void > ( ) ; private _countries$ = new BehaviorSubject < Country [ ] > ( [ ] ) ; private _total$ = new BehaviorSubject < number > ( 0 ) ; private _state : State = { page : 1 , pageSize : 4 , searchTerm : '' , sortColumn : '' , sortDirection : '' , } ; constructor ( private pipe ) { this . _search$ . pipe ( tap ( ( ) => this . _loading$ . next ( true ) ) , debounceTime ( 120 ) , switchMap ( ( ) => this . _search ( ) ) , delay ( 120 ) , tap ( ( ) => this . _loading$ . next ( false ) ) ) . subscribe ( result => { this . _countries$ . next ( result . countries ) ; this . _total$ . next ( result . total ) ; } ) ; this . _search$ . next ( ) ; } get countries$ ( ) { return this . _countries$ . asObservable ( ) ; } get total$ ( ) { return this . _total$ . asObservable ( ) ; } get loading$ ( ) { return this . _loading$ . asObservable ( ) ; } get page ( ) { return this . _state . page ; } set page ( page ) { this . _set ( { page } ) ; } get pageSize ( ) { return this . _state . pageSize ; } set pageSize ( pageSize ) { this . _set ( { pageSize } ) ; } get searchTerm ( ) { return this . _state . searchTerm ; } set searchTerm ( searchTerm ) { this . _set ( { searchTerm } ) ; } set sortColumn ( sortColumn ) { this . _set ( { sortColumn } ) ; } set sortDirection ( sortDirection ) { this . _set ( { sortDirection } ) ; } private _set ( patch ) { Object . assign ( this . _state , patch ) ; this . _search$ . next ( ) ; } private _search ( ) { const { sortColumn , sortDirection , pageSize , page , searchTerm } = this . _state ; let countries = sort ( COUNTRIES , sortColumn , sortDirection ) ; countries = countries . filter ( country => matches ( country , searchTerm , this . pipe ) ) ; const total = countries . length ; countries = countries . slice ( ( page - 1 ) * pageSize , ( page - 1 ) * pageSize + pageSize ) ; return of ( { countries , total } ) ; } }