From fd0730c48ec5ba743dc2fff1987647fb82fd1d88 Mon Sep 17 00:00:00 2001 From: Davide Cavaliere Date: Fri, 17 May 2024 13:00:33 +0200 Subject: [PATCH] chore(core): migrate bind-to-query directives --- .../bind-query-params.abstract.spec.ts | 104 ++++++++++++++++ .../bind-query-params.abstract.ts | 74 +++++++++++ .../bind-query-params.module.ts | 26 ++++ ...ery-params-mat-paginator.directive.spec.ts | 117 ++++++++++++++++++ ...nd-query-params-mat-paginator.directive.ts | 68 ++++++++++ ...nd-query-params-mat-sort.directive.spec.ts | 82 ++++++++++++ .../bind-query-params-mat-sort.directive.ts | 32 +++++ ...ind-query-params-mat-tab.directive.spec.ts | 105 ++++++++++++++++ .../bind-query-params-mat-tab.directive.ts | 37 ++++++ projects/core/src/lib/directives/index.ts | 4 + projects/core/src/public-api.ts | 3 + 11 files changed, 652 insertions(+) create mode 100644 projects/core/src/lib/directives/bind-query-params/bind-query-params.abstract.spec.ts create mode 100644 projects/core/src/lib/directives/bind-query-params/bind-query-params.abstract.ts create mode 100644 projects/core/src/lib/directives/bind-query-params/bind-query-params.module.ts create mode 100644 projects/core/src/lib/directives/bind-query-params/to-mat-paginator/bind-query-params-mat-paginator.directive.spec.ts create mode 100644 projects/core/src/lib/directives/bind-query-params/to-mat-paginator/bind-query-params-mat-paginator.directive.ts create mode 100644 projects/core/src/lib/directives/bind-query-params/to-mat-sort/bind-query-params-mat-sort.directive.spec.ts create mode 100644 projects/core/src/lib/directives/bind-query-params/to-mat-sort/bind-query-params-mat-sort.directive.ts create mode 100644 projects/core/src/lib/directives/bind-query-params/to-mat-tab/bind-query-params-mat-tab.directive.spec.ts create mode 100644 projects/core/src/lib/directives/bind-query-params/to-mat-tab/bind-query-params-mat-tab.directive.ts create mode 100644 projects/core/src/lib/directives/index.ts diff --git a/projects/core/src/lib/directives/bind-query-params/bind-query-params.abstract.spec.ts b/projects/core/src/lib/directives/bind-query-params/bind-query-params.abstract.spec.ts new file mode 100644 index 0000000..f51217a --- /dev/null +++ b/projects/core/src/lib/directives/bind-query-params/bind-query-params.abstract.spec.ts @@ -0,0 +1,104 @@ +import { TestBed } from '@angular/core/testing'; +import { Directive } from '@angular/core'; +import { Subject } from 'rxjs'; +import { Params } from '@angular/router'; +import { BindQueryParamsAbstract } from './bind-query-params.abstract'; +import { Mocked, MockService } from '../../test-utils/mock-service'; +import { ParameterService } from '../../services/paramter/parameter.service'; + +describe('bind-query-params.abstract', () => { + + @Directive({ + // eslint-disable-next-line @angular-eslint/directive-selector + selector: '[testBindQueryParams]', + }) + // eslint-disable-next-line @angular-eslint/directive-class-suffix + class BindQueryParamsImpl extends BindQueryParamsAbstract { + + setInitialSpy = jest.fn(); + + override source$!: Subject; + + constructor( + protected paramService: ParameterService, + ) { + super(paramService, new Subject()); + } + + protected setInitialValue(params: Params) { + this.setInitialSpy(params); + } + } + + let instance: BindQueryParamsImpl; + let parameterService: Mocked; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + providers: [ + BindQueryParamsImpl, + {provide: ParameterService, useClass: MockService(ParameterService)()}, + ], + + }).compileComponents(); + + instance = TestBed.inject(BindQueryParamsImpl); + parameterService = TestBed.inject(ParameterService) as Mocked; + }); + + it('should create', () => { + expect(instance).toBeTruthy(); + }); + + describe('AfterViewInit', () => { + const params = { + x: 'gojira', + y: 'mothra', + }; + + it('should parse and set initial values from query params', () => { + parameterService.getQueryParams.mockReturnValue(params); + instance.ngAfterViewInit(); + + expect(instance.setInitialSpy).toHaveBeenCalledWith(params); + }); + + it('should parse and set new values as query params, when source emits', () => { + instance.ngAfterViewInit(); + const values = { + a: 'ghidorah', + b: 'rodan', + }; + instance.source$.next(values); + + expect(parameterService.setQueryParams).toHaveBeenCalledWith(values); + }); + + describe('when waiting for parent component', () => { + beforeEach(() => { + parameterService.getQueryParams.mockReturnValue(params); + instance.waitForParent = true; + }); + + it('should not set the initial value right away', () => { + instance.ngAfterViewInit(); + expect(instance.setInitialSpy).not.toBeCalled(); + }); + + it('should set the initial value after the parentInitialized input is set to true', () => { + instance.ngAfterViewInit(); + instance.parentInitialized = true; + expect(instance.setInitialSpy).toBeCalledWith(params); + }); + + it('should set the initial value only once', () => { + instance.ngAfterViewInit(); + instance.parentInitialized = true; + instance.parentInitialized = false; + instance.parentInitialized = true; + + expect(instance.setInitialSpy).toBeCalledTimes(1); + }); + }); + }); +}); diff --git a/projects/core/src/lib/directives/bind-query-params/bind-query-params.abstract.ts b/projects/core/src/lib/directives/bind-query-params/bind-query-params.abstract.ts new file mode 100644 index 0000000..0300aee --- /dev/null +++ b/projects/core/src/lib/directives/bind-query-params/bind-query-params.abstract.ts @@ -0,0 +1,74 @@ +import { AfterViewInit, Directive, Input, OnDestroy } from '@angular/core'; +import { Params } from '@angular/router'; +import { filter, map, take, takeUntil, tap } from 'rxjs/operators'; +import { BehaviorSubject, Observable, Subject } from 'rxjs'; +import { ParameterService } from '../../services/paramter/parameter.service'; + +@Directive() +export abstract class BindQueryParamsAbstract implements AfterViewInit, OnDestroy { + + @Input() waitForParent: boolean = false; + + @Input() set parentInitialized(value: boolean) { + this.parentComponentInitialized$.next(value); + } + + private parentComponentInitialized$: BehaviorSubject = new BehaviorSubject(false); + + private destroyed$ = new Subject(); + + constructor( + protected parameterService: ParameterService, + protected source$?: Observable, + ) {} + + ngAfterViewInit(): void { + // subscribe here to make sure that async pipes have been processed already + const params = this.parameterService.getQueryParams(); + const initialValue = this.parseInitialValue(params); + + this.parentComponentInitialized$.pipe( + filter(initialized => this.waitForParent ? initialized : true), + take(1), + tap(() => this.setInitialValue(initialValue)), + takeUntil(this.destroyed$), + ).subscribe(); + + this.source$?.pipe( + map((values) => this.parseValuesToParams(values)), + tap((values) => this.parameterService.setQueryParams(values)), + takeUntil(this.destroyed$), + ).subscribe(); + } + + /** + * Parses query parameters and set them on the bound component + * i.e.: + * - in case of a FormGroup we would need to call formGroup.patchValue + * - in case of a MatPaginator we want to call this.paginator.page.next + * + * @param params query parameters as they're seen when loading the page + * @protected + */ + protected abstract setInitialValue(params: unknown): void; + + /** + * Parses query parameters so that they can be used to be nexted on `source$` + * + * @param params + * @protected + * @returns the parsed query params + */ + protected parseInitialValue(params: Params): Params { + return params; + } + + parseValuesToParams(values: Params): Params { + return values; + } + + ngOnDestroy() { + this.destroyed$.next(); + this.destroyed$.complete(); + } +} diff --git a/projects/core/src/lib/directives/bind-query-params/bind-query-params.module.ts b/projects/core/src/lib/directives/bind-query-params/bind-query-params.module.ts new file mode 100644 index 0000000..804087d --- /dev/null +++ b/projects/core/src/lib/directives/bind-query-params/bind-query-params.module.ts @@ -0,0 +1,26 @@ +import { NgModule } from '@angular/core'; +import { BindQueryParamsMatTabDirective } from './to-mat-tab/bind-query-params-mat-tab.directive'; +import { BindQueryParamsMatSortDirective } from './to-mat-sort/bind-query-params-mat-sort.directive'; +import { BindQueryParamsMatPaginatorDirective } from './to-mat-paginator/bind-query-params-mat-paginator.directive'; +import { MatSortModule } from '@angular/material/sort'; + +const exportables = [ + BindQueryParamsMatTabDirective, + BindQueryParamsMatSortDirective, + BindQueryParamsMatPaginatorDirective, +]; + +@NgModule({ + imports: [ + MatSortModule, + ], + exports: [ + exportables + ], + declarations: [ + exportables + ], + providers: [], +}) +export class BindQueryParamsModule { +} diff --git a/projects/core/src/lib/directives/bind-query-params/to-mat-paginator/bind-query-params-mat-paginator.directive.spec.ts b/projects/core/src/lib/directives/bind-query-params/to-mat-paginator/bind-query-params-mat-paginator.directive.spec.ts new file mode 100644 index 0000000..732518d --- /dev/null +++ b/projects/core/src/lib/directives/bind-query-params/to-mat-paginator/bind-query-params-mat-paginator.directive.spec.ts @@ -0,0 +1,117 @@ +import { Component, ViewChild } from '@angular/core'; +import { ComponentFixture, fakeAsync, TestBed, tick } from '@angular/core/testing'; +import { MatPaginator, MatPaginatorModule, PageEvent } from '@angular/material/paginator'; +import { NoopAnimationsModule } from '@angular/platform-browser/animations'; +import { Mocked, MockService } from '../../../test-utils/mock-service'; +import { ParameterService } from '../../../services/paramter/parameter.service'; +import { StorageService } from '../../../services/storage/storage.service'; +import { BindQueryParamsMatPaginatorDirective } from './bind-query-params-mat-paginator.directive'; + +describe('bind-query-params-mat-paginator', () => { + + @Component({ + selector: 'test-component', + template: ` + + + + `, + }) + class TestComponent { + @ViewChild('paginator') + paginator!: MatPaginator; + + items = new Array(100); + pageIndex = 0; + pageSize = 10; + pageSizeOptions = [ 1, 10, 20 ]; + storageKey = 'sizeKey'; + + onPageChange = jest.fn(); + } + + let fixture: ComponentFixture; + let comp: TestComponent; + let parameterService: Mocked; + let storageService: Mocked; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [ + MatPaginatorModule, + NoopAnimationsModule, + ], + declarations: [ + TestComponent, + BindQueryParamsMatPaginatorDirective, + ], + providers: [ + {provide: ParameterService, useClass: MockService(ParameterService)()}, + {provide: StorageService, useClass: MockService(StorageService)()}, + ], + }).compileComponents(); + + parameterService = TestBed.inject(ParameterService) as Mocked; + parameterService.getQueryParams.mockReturnValue({namespace_pageIndex: '2', namespace_pageSize: '20'}); + + storageService = TestBed.inject(StorageService) as Mocked; + storageService.getStorage.mockReturnValue(100); + + fixture = TestBed.createComponent(TestComponent); + comp = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(comp).toBeTruthy(); + }); + + it('should set initial values from queryParams', fakeAsync(() => { + tick(); + expect(comp.paginator.pageIndex).toEqual(2); + expect(comp.paginator.pageSize).toEqual(20); + expect(comp.onPageChange).toHaveBeenCalledWith({pageIndex: 2, pageSize: 20}); + })); + + it('should set initial page size from local storage, if no queryParams', fakeAsync(() => { + parameterService.getQueryParams.mockReturnValue({}); + fixture = TestBed.createComponent(TestComponent); + comp = fixture.componentInstance; + fixture.detectChanges(); + tick(); + + expect(comp.paginator.pageIndex).toEqual(0); + expect(comp.paginator.pageSize).toEqual(10); + })); + + it('should set query params when page changes', () => { + comp.paginator.nextPage(); + expect(parameterService.setQueryParams).toHaveBeenCalledWith({namespace_pageIndex: 3, namespace_pageSize: 20}); + }); + + it('should set query params when page changes size', () => { + comp.paginator._changePageSize(10); + expect(parameterService.setQueryParams).toHaveBeenCalledWith({namespace_pageIndex: 4, namespace_pageSize: 10}); + }); + + it('should set local storage when page changes size', () => { + comp.paginator._changePageSize(10); + expect(storageService.setStorage).toHaveBeenCalledWith(comp.storageKey, 10); + }); + + it('should set query params when page changes programmatically', () => { + comp.pageIndex = 5; + comp.pageSize = 50; + fixture.detectChanges(); + + expect(parameterService.setQueryParams).toHaveBeenCalledWith({namespace_pageIndex: 5, namespace_pageSize: 50}); + }); +}); diff --git a/projects/core/src/lib/directives/bind-query-params/to-mat-paginator/bind-query-params-mat-paginator.directive.ts b/projects/core/src/lib/directives/bind-query-params/to-mat-paginator/bind-query-params-mat-paginator.directive.ts new file mode 100644 index 0000000..17c558d --- /dev/null +++ b/projects/core/src/lib/directives/bind-query-params/to-mat-paginator/bind-query-params-mat-paginator.directive.ts @@ -0,0 +1,68 @@ +import { Directive, Input, OnChanges, Optional, SimpleChanges } from '@angular/core'; +import { MatPaginator, PageEvent } from '@angular/material/paginator'; +import { Params } from '@angular/router'; +import { BindQueryParamsAbstract } from '../bind-query-params.abstract'; +import { ParameterService } from '../../../services/paramter/parameter.service'; +import { StorageService } from '../../../services/storage/storage.service'; + +@Directive({selector: '[bindQueryParamsMatPaginator]'}) +export class BindQueryParamsMatPaginatorDirective extends BindQueryParamsAbstract implements OnChanges { + + private separator = '_'; + + @Input() pageIndex!: number; + @Input() pageSize!: number; + @Input() queryParamsNamespace!: string; + + // eslint-disable-next-line @angular-eslint/no-input-rename + @Input('sizeStorageKey') storageKey!: string; + + constructor( + protected override parameterService: ParameterService, + protected paginator: MatPaginator, + @Optional() protected storage: StorageService, + ) { + super(parameterService, paginator.page); + } + + ngOnChanges({pageIndex, pageSize, }: SimpleChanges): void { + if (!(pageIndex?.firstChange || pageSize.firstChange)) { + + if (pageIndex.currentValue || pageSize.currentValue) { + this.paginator.page.next({pageIndex: this.pageIndex, pageSize: this.pageSize} as PageEvent); + } + + } + + } + + protected override parseInitialValue(params: Params): { pageSize: number; pageIndex: number|undefined } { + const pageSizeKey = [ this.queryParamsNamespace, 'pageSize' ].join(this.separator); + const pageIndexKey = [ this.queryParamsNamespace, 'pageIndex' ].join(this.separator); + const pageSize = (params[pageSizeKey] ? Number(params[pageSizeKey]) : (this.storageKey ? this.storage.getStorage(this.storageKey) : undefined)) as number; + const pageIndex = params[pageIndexKey] ? Number(params[pageIndexKey]) : undefined; + return {pageSize, pageIndex}; + } + + protected setInitialValue({pageIndex = this.pageIndex, pageSize = this.pageSize}: PageEvent): void { + void Promise.resolve().then(() => { + + this.pageIndex = pageIndex; + this.pageSize = pageSize; + this.paginator.pageIndex = pageIndex; + this.paginator.pageSize = pageSize; + this.paginator.page.next({pageIndex, pageSize} as PageEvent); + }); + } + + override parseValuesToParams({pageIndex, pageSize}: PageEvent): Pick { + if (this.storageKey) { + this.storage.setStorage(this.storageKey, pageSize); + } + + return { + [[ this.queryParamsNamespace, 'pageIndex' ].join(this.separator)]: pageIndex || null, // Remove if 0 + [[ this.queryParamsNamespace, 'pageSize' ].join(this.separator)]: pageSize, + }; + } +} diff --git a/projects/core/src/lib/directives/bind-query-params/to-mat-sort/bind-query-params-mat-sort.directive.spec.ts b/projects/core/src/lib/directives/bind-query-params/to-mat-sort/bind-query-params-mat-sort.directive.spec.ts new file mode 100644 index 0000000..337429d --- /dev/null +++ b/projects/core/src/lib/directives/bind-query-params/to-mat-sort/bind-query-params-mat-sort.directive.spec.ts @@ -0,0 +1,82 @@ +import { Component, ViewChild } from '@angular/core'; +import { ComponentFixture, TestBed } from '@angular/core/testing'; +import { NoopAnimationsModule } from '@angular/platform-browser/animations'; +import { MatSort, MatSortModule } from '@angular/material/sort'; +import { Mocked, MockService } from '../../../test-utils/mock-service'; +import { ParameterService } from '../../../services/paramter/parameter.service'; +import { BindQueryParamsModule } from '../bind-query-params.module'; + +describe('bindQueryParamsMatSort', () => { + + const sortBy = 'columnX'; + const sortOrder = 'desc'; + + @Component({ + selector: 'test-component', + template: ` + + + + + +
XY
+ `, + }) + class TestComponent { + @ViewChild('sort') sort!: MatSort; + } + + let fixture: ComponentFixture; + let component: TestComponent; + let parameterService: Mocked; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [ + MatSortModule, + NoopAnimationsModule, + BindQueryParamsModule, + ], + declarations: [ + TestComponent, + ], + providers: [ + {provide: ParameterService, useClass: MockService(ParameterService)()}, + ], + }).compileComponents(); + + parameterService = TestBed.inject(ParameterService) as Mocked; + parameterService.getQueryParams.mockReturnValue({}); + + fixture = TestBed.createComponent(TestComponent); + component = fixture.componentInstance; + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); + + describe('setInitialValue()', () => { + + it('should set initial sort values on from query parameters', async () => { + parameterService.getQueryParams.mockReturnValue({sortBy, sortOrder}); + fixture.detectChanges(); + await fixture.whenStable(); + expect(component.sort.active).toEqual(sortBy); + expect(component.sort.direction).toEqual(sortOrder); + }); + }); + + describe('parseValuesToParams()', () => { + + it('should update query parameters on sort change', async () => { + fixture.detectChanges(); + component.sort.sortChange.next({active: sortBy, direction: sortOrder}); + fixture.detectChanges(); + await fixture.whenStable(); + expect(parameterService.setQueryParams).toHaveBeenCalledWith({sortBy, sortOrder}); + }); + }); +}); diff --git a/projects/core/src/lib/directives/bind-query-params/to-mat-sort/bind-query-params-mat-sort.directive.ts b/projects/core/src/lib/directives/bind-query-params/to-mat-sort/bind-query-params-mat-sort.directive.ts new file mode 100644 index 0000000..4f062b0 --- /dev/null +++ b/projects/core/src/lib/directives/bind-query-params/to-mat-sort/bind-query-params-mat-sort.directive.ts @@ -0,0 +1,32 @@ +import { Directive, Self } from '@angular/core'; +import { MatSort, Sort, SortDirection } from '@angular/material/sort'; +import { ParameterService } from '../../../services/paramter/parameter.service'; +import { BindQueryParamsAbstract } from '../bind-query-params.abstract'; + +// eslint-disable-next-line @angular-eslint/directive-selector +@Directive({selector: '[bindQueryParamsMatSort]'}) +export class BindQueryParamsMatSortDirective extends BindQueryParamsAbstract { + + constructor( + protected override parameterService: ParameterService, + @Self() protected sort: MatSort, + ) { + super(parameterService, sort.sortChange); + } + + protected setInitialValue({sortBy, sortOrder}: { sortBy: string; sortOrder: SortDirection }): void { + void Promise.resolve().then(() => { + if (sortBy || sortOrder) { + this.sort.sort({id: sortBy, start: sortOrder as 'asc' | 'desc', disableClear: true}); + } + }); + } + + override parseValuesToParams({active, direction}: Sort): { sortBy: string|null; sortOrder: SortDirection|null } { + // Ony write params with both values present + return { + sortBy: (direction && active) ? active : null, + sortOrder: (active && direction) ? direction : null, + }; + } +} diff --git a/projects/core/src/lib/directives/bind-query-params/to-mat-tab/bind-query-params-mat-tab.directive.spec.ts b/projects/core/src/lib/directives/bind-query-params/to-mat-tab/bind-query-params-mat-tab.directive.spec.ts new file mode 100644 index 0000000..88a41d4 --- /dev/null +++ b/projects/core/src/lib/directives/bind-query-params/to-mat-tab/bind-query-params-mat-tab.directive.spec.ts @@ -0,0 +1,105 @@ +import { Component, ViewChild } from '@angular/core'; +import { ComponentFixture, TestBed } from '@angular/core/testing'; +import { NoopAnimationsModule } from '@angular/platform-browser/animations'; +import { MatTabsModule, MatTabGroup } from '@angular/material/tabs'; +import { Mocked, MockService } from '../../../test-utils/mock-service'; +import { ParameterService } from '../../../services/paramter/parameter.service'; +import { BindQueryParamsMatTabDirective } from './bind-query-params-mat-tab.directive'; + +describe('bind-query-params-mat-tab', () => { + + const tabNames = [ 'Tab1', 'Tab2' ]; + + @Component({ + selector: 'test-component', + template: ` + + + my first tab + + + my second tab + + + `, + }) + class TestComponent { + @ViewChild('tabGroup') + tabGroup!: MatTabGroup; + + tabNames = tabNames; + useTabNames = false; + } + + let fixture: ComponentFixture; + let comp: TestComponent; + let parameterService: Mocked; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [ + MatTabsModule, + NoopAnimationsModule, + ], + declarations: [ + TestComponent, + BindQueryParamsMatTabDirective, + ], + providers: [ + {provide: ParameterService, useClass: MockService(ParameterService)()}, + ], + }).compileComponents(); + + parameterService = TestBed.inject(ParameterService) as Mocked; + parameterService.getQueryParams.mockReturnValue({}); + + fixture = TestBed.createComponent(TestComponent); + comp = fixture.componentInstance; + }); + + it('should create', () => { + expect(comp).toBeTruthy(); + }); + + describe('setInitialValue()', () => { + + it('should set initial values: tab indexes', async () => { + parameterService.getQueryParams.mockReturnValue({tab: 1}); + fixture.detectChanges(); // Trigger ngAfterViewInit + fixture.detectChanges(); // Trigger updated to tabGroup.selectedIndex + await fixture.whenStable(); + expect(comp.tabGroup.selectedIndex).toEqual(1); + }); + + it('should set initial values: tab names', async () => { + parameterService.getQueryParams.mockReturnValue({tab: tabNames[1]}); + comp.useTabNames = true; + fixture.detectChanges(); // Trigger ngAfterViewInit + fixture.detectChanges(); // Trigger updated to tabGroup.selectedIndex + await fixture.whenStable(); + expect(comp.tabGroup.selectedIndex).toEqual(1); + }); + }); + + describe('parseValuesToParams()', () => { + + it('should update queryParam on tab change: tab indexes', async () => { + fixture.detectChanges(); + comp.tabGroup.selectedIndexChange.next(1); + fixture.detectChanges(); + await fixture.whenStable(); + expect(parameterService.setQueryParams).toHaveBeenCalledWith({tab: 1}); + }); + + it('should update queryParam on tab change: tab names', async () => { + comp.useTabNames = true; + fixture.detectChanges(); + comp.tabGroup.selectedIndexChange.next(1); + fixture.detectChanges(); + await fixture.whenStable(); + expect(parameterService.setQueryParams).toHaveBeenCalledWith({tab: tabNames[1]}); + }); + }); +}); diff --git a/projects/core/src/lib/directives/bind-query-params/to-mat-tab/bind-query-params-mat-tab.directive.ts b/projects/core/src/lib/directives/bind-query-params/to-mat-tab/bind-query-params-mat-tab.directive.ts new file mode 100644 index 0000000..7d7e4bf --- /dev/null +++ b/projects/core/src/lib/directives/bind-query-params/to-mat-tab/bind-query-params-mat-tab.directive.ts @@ -0,0 +1,37 @@ +import { Directive, Input } from '@angular/core'; +import { MatTabGroup } from '@angular/material/tabs'; +import { Params } from '@angular/router'; +import { BindQueryParamsAbstract } from '../bind-query-params.abstract'; +import { ParameterService } from '../../../services/paramter/parameter.service'; + +// eslint-disable-next-line @angular-eslint/directive-selector +@Directive({selector: '[bindQueryParamsMatTab]'}) +export class BindQueryParamsMatTabDirective extends BindQueryParamsAbstract { + @Input('bindQueryParamsMatTab') + paramName!: string; + + @Input() + tabNames!: string[]; + + constructor( + override parameterService: ParameterService, + protected tabGroup: MatTabGroup, + ) { + super(parameterService, tabGroup.selectedIndexChange); + } + + protected setInitialValue(params: Params): void { + const tab = params[this.paramName] as string; + const index = (Array.isArray(this.tabNames) ? (this.tabNames.includes(tab) ? this.tabNames.indexOf(tab) : 0) : tab) as number; + + this.tabGroup.selectedIndex = index; + } + + override parseValuesToParams(values: Params): Params { + return { + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore + [this.paramName]: Array.isArray(this.tabNames) ? (this.tabNames[values] || values) : values, + }; + } +} diff --git a/projects/core/src/lib/directives/index.ts b/projects/core/src/lib/directives/index.ts new file mode 100644 index 0000000..f3c2fb4 --- /dev/null +++ b/projects/core/src/lib/directives/index.ts @@ -0,0 +1,4 @@ +export * from './bind-query-params/bind-query-params.module'; +export * from './bind-query-params/to-mat-paginator/bind-query-params-mat-paginator.directive'; +export * from './bind-query-params/to-mat-sort/bind-query-params-mat-sort.directive'; +export * from './bind-query-params/to-mat-tab/bind-query-params-mat-tab.directive'; diff --git a/projects/core/src/public-api.ts b/projects/core/src/public-api.ts index 4c318b4..f14f8aa 100644 --- a/projects/core/src/public-api.ts +++ b/projects/core/src/public-api.ts @@ -22,3 +22,6 @@ export * from './lib/test-utils/mock-service'; export * from './lib/test-utils/activated-route.mock'; export * from './lib/utilities/general.utilities'; + +// directives +export * from './lib/directives';