From c552ed2e21d3df6fbcfe842ae85ac5a75006ab85 Mon Sep 17 00:00:00 2001 From: Dan Percic Date: Tue, 10 Aug 2021 16:58:25 +0300 Subject: [PATCH] add listing component --- src/index.ts | 3 +- src/lib/filtering/filter.service.ts | 4 +- src/lib/tables/listing-component.directive.ts | 77 +++++++++++++++++++ .../auto-unsubscribe.directive.ts} | 2 +- 4 files changed, 82 insertions(+), 4 deletions(-) create mode 100644 src/lib/tables/listing-component.directive.ts rename src/lib/{base/auto-unsubscribe.component.ts => utils/auto-unsubscribe.directive.ts} (92%) diff --git a/src/index.ts b/src/index.ts index 0cc5593..849fbdb 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,8 +1,8 @@ export * from './lib/common-ui.module'; -export * from './lib/base/auto-unsubscribe.component'; 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/auto-unsubscribe.directive'; export * from './lib/utils/pipes/humanize.pipe'; export * from './lib/utils/types/utility-types'; export * from './lib/utils/types/tooltip-positions.type'; @@ -21,5 +21,6 @@ export * from './lib/sorting/models/sorting-option.model'; export * from './lib/sorting/models/sorting-order.type'; export * from './lib/search/search.service'; export * from './lib/tables/entities.service'; +export * from './lib/tables/listing-component.directive'; export * from './lib/tables/models/table-column-config.model'; export * from './lib/tables/table-column-name/table-column-name.component'; diff --git a/src/lib/filtering/filter.service.ts b/src/lib/filtering/filter.service.ts index 156bc75..ab30e5e 100644 --- a/src/lib/filtering/filter.service.ts +++ b/src/lib/filtering/filter.service.ts @@ -57,10 +57,10 @@ export class FilterService { } getFilterModels$(filterGroupSlug: string): Observable { - return this.getFilterGroup$(filterGroupSlug).pipe(map(f => f?.filters)); + return this.getGroup$(filterGroupSlug).pipe(map(f => f?.filters)); } - getFilterGroup$(slug: string): Observable { + getGroup$(slug: string): Observable { return this.filterGroups$.pipe(map(all => all.find(f => f.slug === slug))); } diff --git a/src/lib/tables/listing-component.directive.ts b/src/lib/tables/listing-component.directive.ts new file mode 100644 index 0000000..be2410f --- /dev/null +++ b/src/lib/tables/listing-component.directive.ts @@ -0,0 +1,77 @@ +import { Directive, Injector, OnDestroy, Provider } from '@angular/core'; +import { combineLatest, Observable } from 'rxjs'; +import { distinctUntilChanged, map, switchMap } from 'rxjs/operators'; +import { FilterService } from '../filtering/filter.service'; +import { SortingService } from '../sorting/sorting.service'; +import { SortingOrders } from '../sorting/models/sorting-order.type'; +import { Bind } from '../utils/decorators/bind.decorator'; +import { AutoUnsubscribe } from '../utils/auto-unsubscribe.directive'; +import { SearchService } from '../search/search.service'; +import { KeysOf } from '../utils/types/utility-types'; +import { TableColumnConfig } from './models/table-column-config.model'; +import { EntitiesService } from './entities.service'; + +export const DefaultListingServices = new Set().add(FilterService).add(SearchService).add(EntitiesService).add(SortingService); + +@Directive() +export abstract class ListingComponent extends AutoUnsubscribe implements OnDestroy { + readonly filterService = this._injector.get(FilterService); + readonly searchService = this._injector.get>(SearchService); + readonly sortingService = this._injector.get>(SortingService); + readonly entitiesService = this._injector.get>(EntitiesService); + + readonly noMatch$ = this._noMatch$; + readonly sortedDisplayedEntities$ = this._sortedDisplayedEntities$; + + abstract readonly tableColumnConfigs: TableColumnConfig[]; + /** + * Key used in the *trackBy* function with **ngFor* or **cdkVirtualFor* + * and in the default sorting and as the search field + * @protected + */ + protected abstract readonly _primaryKey: KeysOf; + + protected constructor(protected readonly _injector: Injector) { + super(); + setTimeout(() => this.setInitialConfig()); + } + + get allEntities(): T[] { + return this.entitiesService.all; + } + + private get _sortedDisplayedEntities$(): Observable { + const sort = (entities: T[]) => this.sortingService.defaultSort(entities); + const sortedEntities = () => this.entitiesService.displayed$.pipe(map(sort)); + return this.sortingService.sortingOption$.pipe(switchMap(sortedEntities)); + } + + private get _noMatch$(): Observable { + return combineLatest([this.entitiesService.allLength$, this.entitiesService.displayedLength$]).pipe( + map(([hasEntities, hasDisplayedEntities]) => !!hasEntities && !hasDisplayedEntities), + distinctUntilChanged() + ); + } + + setInitialConfig() { + this.sortingService.setSortingOption({ + column: this._primaryKey, + order: SortingOrders.asc + }); + this.searchService.setSearchKey(this._primaryKey); + } + + toggleEntitySelected(event: MouseEvent, entity: T) { + event.stopPropagation(); + return this.entitiesService.select(entity); + } + + isSelected(entity: T): boolean { + return this.entitiesService.isSelected(entity); + } + + @Bind() + trackByPrimaryKey(index: number, item: T) { + return item[this._primaryKey]; + } +} diff --git a/src/lib/base/auto-unsubscribe.component.ts b/src/lib/utils/auto-unsubscribe.directive.ts similarity index 92% rename from src/lib/base/auto-unsubscribe.component.ts rename to src/lib/utils/auto-unsubscribe.directive.ts index dd55e03..38db9a3 100644 --- a/src/lib/base/auto-unsubscribe.component.ts +++ b/src/lib/utils/auto-unsubscribe.directive.ts @@ -5,7 +5,7 @@ import { Subscription } from "rxjs"; * Inherit this class when you need to subscribe to observables in your components */ @Directive() -export abstract class AutoUnsubscribeComponent implements OnDestroy { +export abstract class AutoUnsubscribe implements OnDestroy { private readonly _subscriptions = new Subscription(); /**