add search service and better typings

This commit is contained in:
Dan Percic 2021-08-06 14:30:07 +03:00
parent 3f9915434d
commit be233975d6
7 changed files with 88 additions and 14 deletions

View File

@ -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';

View File

@ -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;

View File

@ -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<typeof controlsConfig>]: string };
@Injectable()
export class SearchService<T extends object> {
readonly searchForm = this._formBuilder.group(controlsConfig);
readonly valueChanges$ = this.searchForm.valueChanges.pipe(
startWith(''),
map((values: FormControls) => values.query)
);
private _searchKey!: KeysOf<T>;
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<T>): void {
this._searchKey = value;
}
reset(): void {
this.searchForm.reset({ query: '' }, { emitEvent: true });
}
private _searchField(entity: T): string {
return ((<unknown>entity[this._searchKey]) as string).toString().toLowerCase();
}
}

View File

@ -1,6 +1,7 @@
import { SortingOrder } from './sorting-order.type';
import { KeysOf } from '../../utils/types/utility-types';
export interface SortingOption {
export interface SortingOption<T extends object> {
readonly order: SortingOrder;
readonly column: string;
readonly column: KeysOf<T>;
}

View File

@ -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<T extends object> implements PipeTransform {
constructor(private readonly _sortingService: SortingService<T>) {}
transform<T>(value: T[], order: SortingOrder, column: string): T[] {
transform(value: T[], order: SortingOrder, column: KeysOf<T>): T[] {
return this._sortingService.sort(value, order, column);
}
}

View File

@ -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<SortingOption | undefined>(undefined);
export class SortingService<T extends object> {
private readonly _sortingOption$ = new BehaviorSubject<SortingOption<T> | undefined>(undefined);
readonly sortingOption$ = this._sortingOption$.asObservable();
get sortingOption(): SortingOption | undefined {
get sortingOption(): SortingOption<T> | undefined {
return this._sortingOption$.getValue();
}
setSortingOption(value: SortingOption): void {
setSortingOption(value: SortingOption<T>): void {
this._sortingOption$.next(value);
}
sort<T>(values: T[], order?: SortingOrder, column?: string): T[] {
sort(values: T[], order?: SortingOrder, column?: KeysOf<T>): T[] {
if (!values || values.length <= 1 || !order) return values;
if (!column) {
@ -31,11 +32,11 @@ export class SortingService {
return orderBy(values, [column], [order]);
}
defaultSort<T>(values: T[]): T[] {
defaultSort(values: T[]): T[] {
return this.sort(values, this.sortingOption?.order, this.sortingOption?.column);
}
toggleSort(column: string): void {
toggleSort(column: KeysOf<T>): void {
const sameColumn = this.sortingOption?.column === column;
const order = sameColumn ? SortingOrders.inverseOf(this.sortingOption?.order) : SortingOrders.asc;

View File

@ -1,5 +1,25 @@
export type KeysOf<T> = 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<Object>;
*/
export type KeysOf<T extends object> = {
[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<Object>;
*/
export type ValuesOf<T extends object> = T[KeysOf<T>];
/**