Skip to content

Commit a6f664c

Browse files
authored
feat!: replace implementations with RouterStore and provider factories (#273)
# Features - Remove `LocalRouterStore` from the public API - Add `provideLocalRouterStore` to the public API - Remove `GlobalRouterStore` from the public API - Add `provideGlobalRouterStore` to the public API - Add `RouterStore` to the public API # Refactorings - Rename `RouterComponentStore` to `RouterStore` and decorate with `Injectable` - Remove unused `ComponentStore` port of `@ngrx/router-store` Fixes #272 BREAKING CHANGE: `LocalRouterStore` is replaced by `RouterStore` and `provideLocalRouterStore`. `GlobalRouterStore` is replaced by `RouterStore` and `provideGlobalRouterStore`.
1 parent d0cf4f6 commit a6f664c

25 files changed

+234
-1133
lines changed

README.md

Lines changed: 50 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ Published with partial Ivy compilation.
1515

1616
## API
1717

18-
Two router stores are available and implement the same public API:
18+
A `RouterStore` service has the following public properties:
1919

2020
| API | Description |
2121
| ----------------------------------------------------------- | ------------------------------------------ |
@@ -28,52 +28,86 @@ Two router stores are available and implement the same public API:
2828
| selectQueryParam<TValue>(param: string): Observable<TValue> | Select the specified query parameter. |
2929
| selectRouteParam<TValue>(param: string): Observable<TValue> | Select the specified route paramter. |
3030

31-
The `GlobalRouterStore` is never destroyed but can be injected in any class.
31+
A `RouterStore` service is provided by using either `provideGlobalRouterStore` or `provideLocalRouterStore`.
3232

33-
The `LocalRouterStore` requires a component-level provider, follows the
33+
The _global_ `RouterStore` service is provided in a root environment injector and is never destroyed but can be injected in any class.
34+
35+
A _local_ `RouterStore` requires a component-level provider, follows the
3436
lifecycle of that component, and can be injected in declarables as well as
3537
other component-level services.
3638

37-
### GlobalRouterStore
39+
### Global router store
3840

39-
An application-wide router store. Can be injected in any class. Implicitly
40-
provided in the root module injector.
41+
An application-wide router store. Can be injected in any class. Provide
42+
in a root environment injector by using `provideGlobalRouterStore`.
4143

4244
Usage:
4345

44-
```ts
46+
```typescript
47+
// app.module.ts
4548
// (...)
46-
import { GlobalRouterStore } from '@ngworker/router-component-store';
49+
import { provideGlobalRouterStore } from '@ngworker/router-component-store';
50+
51+
@NgModule({
52+
// (...)
53+
providers: [provideGlobalRouterStore()],
54+
})
55+
export class AppModule {}
56+
```
57+
58+
```typescript
59+
// hero.service.ts
60+
// (...)
61+
import { RouterStore } from '@ngworker/router-component-store';
4762

4863
@Injectable({
4964
providedIn: 'root',
5065
})
5166
export class HeroService {
52-
activeHeroId$: Observable<number> = this.routerStore.selectQueryParam('id');
67+
activeHeroId$: Observable<string> = this.routerStore.selectQueryParam('id');
68+
69+
constructor(private routerStore: RouterStore) {}
70+
}
71+
```
72+
73+
```typescript
74+
// hero-detail.component.ts
75+
// (...)
76+
import { RouterStore } from '@ngworker/router-component-store';
77+
78+
@Component({
79+
// (...)
80+
})
81+
export class HeroDetailComponent {
82+
heroId$: Observable<string> = this.routerStore.selectQueryParam('id');
5383

54-
constructor(private routerStore: GlobalRouterStore) {}
84+
constructor(private routerStore: RouterStore) {}
5585
}
5686
```
5787

58-
### LocalRouterStore
88+
### Local router store
5989

6090
A component-level router store. Can be injected in any directive, component,
6191
pipe, or component-level service. Explicitly provided in a component sub-tree
6292
using `Component.providers` or `Component.viewProviders`.
6393

6494
Usage:
6595

66-
```ts
96+
```typescript
97+
// hero-detail.component.ts
6798
// (...)
68-
import { LocalRouterStore } from '@ngworker/router-component-store';
99+
import {
100+
provideLocalRouterStore,
101+
RouterStore,
102+
} from '@ngworker/router-component-store';
69103

70104
@Component({
71105
// (...)
72-
providers: [LocalRouterStore],
106+
providers: [provideLocalRouterStore()],
73107
})
74108
export class HeroDetailComponent {
75-
heroId$: Observable<number> = this.routerStore.selectQueryParam('id');
109+
heroId$: Observable<string> = this.routerStore.selectQueryParam('id');
76110

77-
constructor(private routerStore: LocalRouterStore) {}
111+
constructor(private routerStore: RouterStore) {}
78112
}
79113
```
Lines changed: 5 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,11 @@
1-
// // RouterStore state
2-
// export { RouterStore } from './lib/router-store/router-store';
3-
4-
// // RouterStore configuration
5-
// export { RouterStoreModule } from './lib/router-store/router-store.module';
6-
// export { RouterStoreConfig } from './lib/router-store/router-store-config';
7-
8-
// // RouterStore events
9-
// export * from './lib/router-store/router-store-events/router-store-cancel-event';
10-
// export * from './lib/router-store/router-store-events/router-store-error-event';
11-
// export * from './lib/router-store/router-store-events/router-store-event';
12-
// export * from './lib/router-store/router-store-events/router-store-navigated-event';
13-
// export * from './lib/router-store/router-store-events/router-store-navigation-event';
14-
// export * from './lib/router-store/router-store-events/router-store-request-event';
15-
161
// GlobalRouterStore
17-
export * from './lib/global-router-store/global-router-store';
2+
export * from './lib/global-router-store/provide-global-router-store';
183

194
// LocalRouterStore
20-
export * from './lib/local-router-store/local-router-store';
5+
export * from './lib/local-router-store/provide-local-router-store';
6+
7+
// RouterStore
8+
export * from './lib/router-store';
219

2210
// Serializable route state
2311
export { MinimalActivatedRouteSnapshot } from './lib/@ngrx/router-store/minimal_serializer';

packages/router-component-store/src/lib/global-router-store/global-router-component-store.ts

Lines changed: 0 additions & 87 deletions
This file was deleted.

packages/router-component-store/src/lib/global-router-store/global-router-store-selectors.spec.ts

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,9 @@ import { TestBed } from '@angular/core/testing';
33
import { Router, Routes } from '@angular/router';
44
import { RouterTestingModule } from '@angular/router/testing';
55
import { firstValueFrom } from 'rxjs';
6-
6+
import { RouterStore } from '../router-store';
77
import { GlobalRouterStore } from './global-router-store';
8+
import { provideGlobalRouterStore } from './provide-global-router-store';
89

910
@Component({
1011
template: '<router-outlet></router-outlet>',
@@ -34,17 +35,18 @@ describe(`${GlobalRouterStore.name} selectors`, () => {
3435
TestBed.configureTestingModule({
3536
declarations: [DummyAppComponent, DummyLoginComponent],
3637
imports: [RouterTestingModule.withRoutes(routes)],
38+
providers: [provideGlobalRouterStore()],
3739
});
3840

3941
const rootFixture = TestBed.createComponent(DummyAppComponent);
4042
rootFixture.autoDetectChanges(true);
4143

4244
router = TestBed.inject(Router);
43-
store = TestBed.inject(GlobalRouterStore);
45+
store = TestBed.inject(RouterStore);
4446
});
4547

4648
let router: Router;
47-
let store: GlobalRouterStore;
49+
let store: RouterStore;
4850

4951
it('exposes a selector for the current route', async () => {
5052
await router.navigateByUrl('/login/etyDDwAAQBAJ?ref=ngrx.io#test-fragment');
Lines changed: 72 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1,44 +1,84 @@
11
import { Injectable } from '@angular/core';
2-
import { Data, Params } from '@angular/router';
3-
import { Observable } from 'rxjs';
2+
import { Data, Params, Router } from '@angular/router';
3+
import { ComponentStore } from '@ngrx/component-store';
4+
import { map, Observable } from 'rxjs';
5+
import {
6+
MinimalActivatedRouteSnapshot,
7+
MinimalRouterStateSerializer,
8+
MinimalRouterStateSnapshot,
9+
} from '../@ngrx/router-store/minimal_serializer';
10+
import { RouterStore } from '../router-store';
411

5-
import { MinimalActivatedRouteSnapshot } from '../@ngrx/router-store/minimal_serializer';
6-
import { RouterComponentStore } from '../router-component-store';
7-
import { GlobalRouterComponentStore } from './global-router-component-store';
12+
interface GlobalRouterState {
13+
readonly routerState: MinimalRouterStateSnapshot;
14+
}
815

9-
@Injectable({
10-
providedIn: 'root',
11-
})
12-
export class GlobalRouterStore implements RouterComponentStore {
13-
#store: GlobalRouterComponentStore;
16+
@Injectable()
17+
export class GlobalRouterStore
18+
extends ComponentStore<GlobalRouterState>
19+
implements RouterStore
20+
{
21+
#routerState$: Observable<MinimalRouterStateSnapshot> = this.select(
22+
(state) => state.routerState
23+
);
24+
#rootRoute$: Observable<MinimalActivatedRouteSnapshot> = this.select(
25+
this.#routerState$,
26+
(routerState) => routerState.root
27+
);
1428

15-
get currentRoute$(): Observable<MinimalActivatedRouteSnapshot> {
16-
return this.#store.currentRoute$;
17-
}
18-
get fragment$(): Observable<string | null> {
19-
return this.#store.fragment$;
20-
}
21-
get queryParams$(): Observable<Params> {
22-
return this.#store.queryParams$;
23-
}
24-
get routeData$(): Observable<Data> {
25-
return this.#store.routeData$;
26-
}
27-
get routeParams$(): Observable<Params> {
28-
return this.#store.routeParams$;
29-
}
30-
get url$(): Observable<string> {
31-
return this.#store.url$;
32-
}
29+
readonly currentRoute$: Observable<MinimalActivatedRouteSnapshot> =
30+
this.select(this.#rootRoute$, (route) => {
31+
while (route.firstChild) {
32+
route = route.firstChild;
33+
}
34+
35+
return route;
36+
});
37+
readonly fragment$: Observable<string | null> = this.select(
38+
this.#rootRoute$,
39+
(route) => route.fragment
40+
);
41+
readonly queryParams$: Observable<Params> = this.select(
42+
this.#rootRoute$,
43+
(route) => route.queryParams
44+
);
45+
readonly routeData$: Observable<Data> = this.select(
46+
this.currentRoute$,
47+
(route) => route.data
48+
);
49+
readonly routeParams$: Observable<Params> = this.select(
50+
this.currentRoute$,
51+
(route) => route.params
52+
);
53+
readonly url$: Observable<string> = this.select(
54+
this.#routerState$,
55+
(routerState) => routerState.url
56+
);
57+
58+
constructor(router: Router, serializer: MinimalRouterStateSerializer) {
59+
super({
60+
routerState: serializer.serialize(router.routerState.snapshot),
61+
});
3362

34-
constructor(store: GlobalRouterComponentStore) {
35-
this.#store = store;
63+
this.#updateRouterState(
64+
router.events.pipe(
65+
map(() => serializer.serialize(router.routerState.snapshot))
66+
)
67+
);
3668
}
3769

70+
#updateRouterState = this.updater<MinimalRouterStateSnapshot>(
71+
(state, routerState): GlobalRouterState => ({
72+
...state,
73+
routerState,
74+
})
75+
);
76+
3877
selectQueryParam<TValue>(param: string): Observable<TValue> {
39-
return this.#store.selectQueryParam(param);
78+
return this.select(this.queryParams$, (params) => params[param]);
4079
}
80+
4181
selectRouteParam<TValue>(param: string): Observable<TValue> {
42-
return this.#store.selectRouteParam(param);
82+
return this.select(this.routeParams$, (params) => params[param]);
4383
}
4484
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
import { ClassProvider, Provider } from '@angular/core';
2+
import { RouterStore } from '../router-store';
3+
import { GlobalRouterStore } from './global-router-store';
4+
5+
export function provideGlobalRouterStore(): Provider {
6+
const globalRouterStoreProvider: ClassProvider = {
7+
provide: RouterStore,
8+
useClass: GlobalRouterStore,
9+
};
10+
11+
return globalRouterStoreProvider;
12+
}

0 commit comments

Comments
 (0)