From be233975d650ae196871776930b024dcd19869ee Mon Sep 17 00:00:00 2001 From: Dan Percic Date: Fri, 6 Aug 2021 14:30:07 +0300 Subject: [PATCH] add search service and better typings --- src/index.ts | 2 + src/lib/filtering/filter-utils.ts | 2 +- src/lib/search/search.service.ts | 49 +++++++++++++++++++ .../sorting/models/sorting-option.model.ts | 5 +- src/lib/sorting/sort-by.pipe.ts | 7 +-- src/lib/sorting/sorting.service.ts | 15 +++--- src/lib/utils/types/utility-types.ts | 22 ++++++++- 7 files changed, 88 insertions(+), 14 deletions(-) create mode 100644 src/lib/search/search.service.ts diff --git a/src/index.ts b/src/index.ts index 11f2205..af05163 100644 --- a/src/index.ts +++ b/src/index.ts @@ -4,6 +4,7 @@ export * from './lib/buttons/icon-button/icon-button.type'; export * from './lib/buttons/icon-button/icon-button.component'; export * from './lib/utils/functions'; export * from './lib/utils/pipes/humanize.pipe'; +export * from './lib/utils/types/utility-types'; export * from './lib/utils/types/tooltip-positions.type'; export * from './lib/utils/decorators/required.decorator'; export * from './lib/buttons/circle-button/circle-button.type'; @@ -17,3 +18,4 @@ export * from './lib/sorting/sort-by.pipe'; export * from './lib/sorting/sorting.service'; export * from './lib/sorting/models/sorting-option.model'; export * from './lib/sorting/models/sorting-order.type'; +export * from './lib/search/search.service'; diff --git a/src/lib/filtering/filter-utils.ts b/src/lib/filtering/filter-utils.ts index 2327bdb..05bdb94 100644 --- a/src/lib/filtering/filter-utils.ts +++ b/src/lib/filtering/filter-utils.ts @@ -38,7 +38,7 @@ export function handleCheckedValue(filter: NestedFilter) { } } -export function checkFilter(entity: any, filters: NestedFilter[], validate: Function, validateArgs = [], matchAll: boolean = false) { +export function checkFilter(entity: any, filters: NestedFilter[], validate: Function, validateArgs: any = [], matchAll: boolean = false) { const hasChecked = filters.find(f => f.checked); if (!hasChecked) return true; diff --git a/src/lib/search/search.service.ts b/src/lib/search/search.service.ts new file mode 100644 index 0000000..3f1d4f3 --- /dev/null +++ b/src/lib/search/search.service.ts @@ -0,0 +1,49 @@ +import { Injectable } from '@angular/core'; +import { FormBuilder } from '@angular/forms'; +import { map, startWith } from 'rxjs/operators'; +import { KeysOf } from '../utils/types/utility-types'; + +const controlsConfig = { + query: [''] +} as const; + +type FormControls = { [key in KeysOf]: string }; + +@Injectable() +export class SearchService { + readonly searchForm = this._formBuilder.group(controlsConfig); + readonly valueChanges$ = this.searchForm.valueChanges.pipe( + startWith(''), + map((values: FormControls) => values.query) + ); + private _searchKey!: KeysOf; + + constructor(private readonly _formBuilder: FormBuilder) {} + + get searchValue(): string { + return this.searchForm.get('query')?.value; + } + + set searchValue(value: string) { + this.searchForm.patchValue({ query: value }); + } + + searchIn(entities: T[]) { + if (!this._searchKey) return entities; + + const searchValue = this.searchValue.toLowerCase(); + return entities.filter(entity => this._searchField(entity).includes(searchValue)); + } + + setSearchKey(value: KeysOf): void { + this._searchKey = value; + } + + reset(): void { + this.searchForm.reset({ query: '' }, { emitEvent: true }); + } + + private _searchField(entity: T): string { + return ((entity[this._searchKey]) as string).toString().toLowerCase(); + } +} diff --git a/src/lib/sorting/models/sorting-option.model.ts b/src/lib/sorting/models/sorting-option.model.ts index d15a158..910c2c1 100644 --- a/src/lib/sorting/models/sorting-option.model.ts +++ b/src/lib/sorting/models/sorting-option.model.ts @@ -1,6 +1,7 @@ import { SortingOrder } from './sorting-order.type'; +import { KeysOf } from '../../utils/types/utility-types'; -export interface SortingOption { +export interface SortingOption { readonly order: SortingOrder; - readonly column: string; + readonly column: KeysOf; } diff --git a/src/lib/sorting/sort-by.pipe.ts b/src/lib/sorting/sort-by.pipe.ts index 872ce2a..d198efd 100644 --- a/src/lib/sorting/sort-by.pipe.ts +++ b/src/lib/sorting/sort-by.pipe.ts @@ -1,12 +1,13 @@ import { Pipe, PipeTransform } from '@angular/core'; import { SortingService } from './sorting.service'; import { SortingOrder } from './models/sorting-order.type'; +import { KeysOf } from '../utils/types/utility-types'; @Pipe({ name: 'sortBy' }) -export class SortByPipe implements PipeTransform { - constructor(private readonly _sortingService: SortingService) {} +export class SortByPipe implements PipeTransform { + constructor(private readonly _sortingService: SortingService) {} - transform(value: T[], order: SortingOrder, column: string): T[] { + transform(value: T[], order: SortingOrder, column: KeysOf): T[] { return this._sortingService.sort(value, order, column); } } diff --git a/src/lib/sorting/sorting.service.ts b/src/lib/sorting/sorting.service.ts index 3c56e1c..2ac25fb 100644 --- a/src/lib/sorting/sorting.service.ts +++ b/src/lib/sorting/sorting.service.ts @@ -3,23 +3,24 @@ import { orderBy } from 'lodash'; import { BehaviorSubject } from 'rxjs'; import { SortingOption } from './models/sorting-option.model'; import { SortingOrder, SortingOrders } from './models/sorting-order.type'; +import { KeysOf } from '../utils/types/utility-types'; @Injectable({ providedIn: 'root' }) -export class SortingService { - private readonly _sortingOption$ = new BehaviorSubject(undefined); +export class SortingService { + private readonly _sortingOption$ = new BehaviorSubject | undefined>(undefined); readonly sortingOption$ = this._sortingOption$.asObservable(); - get sortingOption(): SortingOption | undefined { + get sortingOption(): SortingOption | undefined { return this._sortingOption$.getValue(); } - setSortingOption(value: SortingOption): void { + setSortingOption(value: SortingOption): void { this._sortingOption$.next(value); } - sort(values: T[], order?: SortingOrder, column?: string): T[] { + sort(values: T[], order?: SortingOrder, column?: KeysOf): T[] { if (!values || values.length <= 1 || !order) return values; if (!column) { @@ -31,11 +32,11 @@ export class SortingService { return orderBy(values, [column], [order]); } - defaultSort(values: T[]): T[] { + defaultSort(values: T[]): T[] { return this.sort(values, this.sortingOption?.order, this.sortingOption?.column); } - toggleSort(column: string): void { + toggleSort(column: KeysOf): void { const sameColumn = this.sortingOption?.column === column; const order = sameColumn ? SortingOrders.inverseOf(this.sortingOption?.order) : SortingOrders.asc; diff --git a/src/lib/utils/types/utility-types.ts b/src/lib/utils/types/utility-types.ts index 82a2d1f..88435c7 100644 --- a/src/lib/utils/types/utility-types.ts +++ b/src/lib/utils/types/utility-types.ts @@ -1,5 +1,25 @@ -export type KeysOf = keyof T; +/** + * KeysOf + * @desc Get union type of keys in object type `T` + * @example + * type Object = {name: string; setName: (name: string) => void; someKeys?: string; someFn?: (...args: any) => any;}; + * + * // Expect: "name | setName | someKeys | someFn" + * type Keys = KeysOf; + */ +export type KeysOf = { + [K in keyof T]: K; +}[keyof T]; +/** + * ValuesOf + * @desc Get union type of values in object type `T` + * @example + * const Object = {bar: "some bar", foo: "some foo"}; + * + * // Expect: "some bar | some foo" + * type Values = ValuesOf; + */ export type ValuesOf = T[KeysOf]; /**