From 3bf5ed36747efdb69e2f7469a3e24571697821ca Mon Sep 17 00:00:00 2001 From: Lars Gyrup Brink Nielsen Date: Thu, 18 Nov 2021 15:51:45 +0100 Subject: [PATCH] feat: harden API --- README.md | 75 +++++++++++++++++++ .../global-router-store.ts | 28 +++---- .../local-router-store/local-router-store.ts | 10 ++- .../src/lib/router-component-store.ts | 15 ++++ 4 files changed, 112 insertions(+), 16 deletions(-) create mode 100644 packages/router-component-store/src/lib/router-component-store.ts diff --git a/README.md b/README.md index 2254b5c..010fbd5 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,78 @@ # @ngworker/router-component-store Angular Router-connecting NgRx component stores. + +## Compatibility + +Required peer dependencies: + +- Angular >=12.2 +- NgRx Component Store >=12.0 +- RxJS >=7.0 + +Published with partial Ivy compilation. + +## API + +Two router stores are available and implement the same public API: + +| API | Description | +| ----------------------------------------------------------- | ------------------------------------------ | +| currentRoute$: Observable | Select the current route. | +| fragment$: Observable | Select the current route fragment. | +| queryParams$: Observable | Select the current route query parameters. | +| routeData$: Observable | Select the current route data. | +| routeParams$: Observable | Select the current route parameters. | +| url$: Observable | Select the current URL. | +| selectQueryParam(param: string): Observable | Select the specified query parameter. | +| selectRouteParam(param: string): Observable | Select the specified route paramter. | + +The `GlobalRouterStore` is never destroyed but can be injected in any class. + +The `LocalRouterStore` requires a component-level provider, follows the +lifecycle of that component, and can be injected in declarables as well as +other component-level services. + +### GlobalRouterStore + +An application-wide router store. Can be injected in any class. Implicitly +provided in the root module injector. + +Usage: + +```ts +// (...) +import { GlobalRouterStore } from '@ngworker/router-component-store'; + +@Injectable({ + providedIn: 'root', +}) +export class HeroService { + activeHeroId$: Observable = this.routerStore.selectQueryParam('id'); + + constructor(private routerStore: GlobalRouterStore) {} +} +``` + +### LocalRouterStore + +A component-level router store. Can be injected in any directive, component, +pipe, or component-level service. Explicitly provided in a component sub-tree +using `Component.providers` or `Component.viewProviders`. + +Usage: + +```ts +// (...) +import { LocalRouterStore } from '@ngworker/router-component-store'; + +@Component({ + // (...) + providers: [LocalRouterStore], +}) +export class HeroDetailComponent { + heroId$: Observable = this.routerStore.selectQueryParam('id'); + + constructor(private routerStore: LocalRouterStore) {} +} +``` diff --git a/packages/router-component-store/src/lib/global-router-store/global-router-store.ts b/packages/router-component-store/src/lib/global-router-store/global-router-store.ts index bf93d3c..55005e6 100644 --- a/packages/router-component-store/src/lib/global-router-store/global-router-store.ts +++ b/packages/router-component-store/src/lib/global-router-store/global-router-store.ts @@ -8,6 +8,7 @@ import { MinimalRouterStateSerializer, MinimalRouterStateSnapshot, } from '../@ngrx/router-store/minimal_serializer'; +import { RouterComponentStore } from '../router-component-store'; interface GlobalRouterStoreState { readonly routerState: MinimalRouterStateSnapshot; @@ -16,7 +17,10 @@ interface GlobalRouterStoreState { @Injectable({ providedIn: 'root', }) -export class GlobalRouterStore extends ComponentStore { +export class GlobalRouterStore + extends ComponentStore + implements RouterComponentStore +{ #routerState$: Observable = this.select( (state) => state.routerState ); @@ -25,33 +29,31 @@ export class GlobalRouterStore extends ComponentStore { (routerState) => routerState.root ); - currentRoute$: Observable = this.select( - this.#rootRoute$, - (route) => { + readonly currentRoute$: Observable = + this.select(this.#rootRoute$, (route) => { while (route.firstChild) { route = route.firstChild; } return route; - } - ); - fragment$: Observable = this.select( + }); + readonly fragment$: Observable = this.select( this.#rootRoute$, (route) => route.fragment ); - queryParams$: Observable = this.select( + readonly queryParams$: Observable = this.select( this.#rootRoute$, (route) => route.queryParams ); - routeData$: Observable = this.select( + readonly routeData$: Observable = this.select( this.currentRoute$, (route) => route.data ); - routeParams$: Observable = this.select( + readonly routeParams$: Observable = this.select( this.currentRoute$, (route) => route.params ); - url$: Observable = this.select( + readonly url$: Observable = this.select( this.#routerState$, (routerState) => routerState.url ); @@ -69,11 +71,11 @@ export class GlobalRouterStore extends ComponentStore { } selectQueryParam(param: string): Observable { - return this.queryParams$.pipe(map((params) => params[param])); + return this.select(this.queryParams$, (params) => params[param]); } selectRouteParam(param: string): Observable { - return this.routeParams$.pipe(map((params) => params[param])); + return this.select(this.routeParams$, (params) => params[param]); } #updateRouterState = this.updater( diff --git a/packages/router-component-store/src/lib/local-router-store/local-router-store.ts b/packages/router-component-store/src/lib/local-router-store/local-router-store.ts index 1971696..e1cda8f 100644 --- a/packages/router-component-store/src/lib/local-router-store/local-router-store.ts +++ b/packages/router-component-store/src/lib/local-router-store/local-router-store.ts @@ -8,13 +8,17 @@ import { MinimalRouterStateSerializer, MinimalRouterStateSnapshot, } from '../@ngrx/router-store/minimal_serializer'; +import { RouterComponentStore } from '../router-component-store'; interface LocalRouterStoreState { readonly routerState: MinimalRouterStateSnapshot; } @Injectable() -export class LocalRouterStore extends ComponentStore { +export class LocalRouterStore + extends ComponentStore + implements RouterComponentStore +{ #routerState$: Observable = this.select( (state) => state.routerState ); @@ -64,11 +68,11 @@ export class LocalRouterStore extends ComponentStore { } selectQueryParam(param: string): Observable { - return this.queryParams$.pipe(map((params) => params[param])); + return this.select(this.queryParams$, (params) => params[param]); } selectRouteParam(param: string): Observable { - return this.routeParams$.pipe(map((params) => params[param])); + return this.select(this.routeParams$, (params) => params[param]); } #updateRouterState = this.updater( diff --git a/packages/router-component-store/src/lib/router-component-store.ts b/packages/router-component-store/src/lib/router-component-store.ts new file mode 100644 index 0000000..636f7b2 --- /dev/null +++ b/packages/router-component-store/src/lib/router-component-store.ts @@ -0,0 +1,15 @@ +import { Data, Params } from '@angular/router'; +import { Observable } from 'rxjs'; + +import { MinimalActivatedRouteSnapshot } from './@ngrx/router-store/minimal_serializer'; + +export abstract class RouterComponentStore { + abstract readonly currentRoute$: Observable; + abstract readonly fragment$: Observable; + abstract readonly queryParams$: Observable; + abstract readonly routeData$: Observable; + abstract readonly routeParams$: Observable; + abstract readonly url$: Observable; + abstract selectQueryParam(param: string): Observable; + abstract selectRouteParam(param: string): Observable; +}