15.0.0-rc.0
Pre-releaseFeatures
LocalRouterStore
matchesActivatedRoute
more closely (#309)- Use
ActivatedRoute
to serialize the router state for the local router store implementation (LocalRouterStore
) LocalRouterStore.currentRoute$
matchesActivatedRoute.snapshot
- Use
- Remove optional type parameter from
RouterStore#selectRouteData
(#316) - Replace
MinimalRouteData
withStrictRouteData
(#319) - Change
RouterStore#routeData$
andMinimalActivatedRouteSnapshot#data
types fromData
toStrictRouteData
(#319) - Use strict and immutable route parameters (#319, #321)
- Use strict and immutable query parameters (#320)
BREAKING CHANGES
LocalRouterStore.currentRoute$
matches ActivatedRoute.snapshot
This change in implementation will make the local router store more closely match ActivatedRoute
while the global router store matches NgRx Router Store selectors. Through complex route configurations, the router store implementations are exercised to identify edge case differences between them and any breaking changes introduced to the local router store.
BEFORE:
// URL: /parent/child/grandchild
@Component({
/* (...) */
providers: [provideLocalRouterStore()],
})
export class ChildComponent implements OnInit {
#route = inject(ActivatedRoute);
#routerStore = inject(RouterStore);
ngOnInit() {
const currentRouteSnapshot = this.#route.snapshot;
console.log(currentRouteSnapshot.routeConfig.path);
// -> "child"
console.log(currentRouteSnapshot.url[0].path);
// -> "child"
firstValueFrom(this.#routerStore.currentRoute$).then((currentRoute) => {
console.log(currentRoute.routeConfig.path);
// -> "grandchild"
console.log(currentRoute.url[0].path);
// -> "grandchild"
});
}
}
AFTER:
// URL: /parent/child/grandchild
@Component({
/* (...) */
providers: [provideLocalRouterStore()],
})
export class ChildComponent implements OnInit {
#route = inject(ActivatedRoute);
#routerStore = inject(RouterStore);
ngOnInit() {
const currentRouteSnapshot = this.#route.snapshot;
console.log(currentRouteSnapshot.routeConfig.path);
// -> "child"
console.log(currentRouteSnapshot.url[0].path);
// -> "child"
firstValueFrom(this.#routerStore.currentRoute$).then((currentRoute) => {
console.log(currentRoute.routeConfig.path);
// -> "child"
console.log(currentRoute.url[0].path);
// -> "child"
});
}
}
The type parameter is removed from RouterStore#selectRouteData
for stricter typing and to enforce coercion
BEFORE:
// heroes.component.ts
// (...)
import { RouterStore } from '@ngworker/router-component-store';
@Component({
// (...)
})
export class HeroesComponent {
#routerStore = inject(RouterStore);
limit$ = this.#routerStore.selectRouteData<number>('limit');
}
AFTER:
// heroes.component.ts
// (...)
import { RouterStore } from '@ngworker/router-component-store';
@Component({
// (...)
})
export class HeroesComponent {
#routerStore = inject(RouterStore);
limit$ = this.#routerStore.selectRouteData('limit').pipe(x => Number(x));
The RouterStore#routeData$
selector emits StrictRouteData
instead of Data
BEFORE:
// heroes.component.ts
// (...)
import { RouterStore } from '@ngworker/router-component-store';
@Component({
// (...)
})
export class HeroesComponent {
#routerStore = inject(RouterStore);
limit$: Observable<number> = this.#routerStore.routeData$.pipe(
map((routeData) => routeData['limit'])
);
}
AFTER:
// heroes.component.ts
// (...)
import { RouterStore } from '@ngworker/router-component-store';
@Component({
// (...)
})
export class HeroesComponent {
#routerStore = inject(RouterStore);
limit$: Observable<number> = this.#routerStore.routeData$.pipe(
map(routeData => routeData['limit']),
map(x => Number(x))
);
RouterStore#routeParams$
and MinimalActivatedRouteSnapshot#params
use StrictRouteData
instead of Params
. Members are read-only and of type string | undefined
instead of any
TypeScript will fail to compile application code that has assumed a route type parameter type other than string | undefined
.
BEFORE:
// heroes.component.ts
// (...)
import { RouterStore } from '@ngworker/router-component-store';
@Component({
// (...)
})
export class DashboardComponent {
#routerStore = inject(RouterStore);
limit$: Observable<number> = this.#routerStore.routeParams$.pipe(
map((params) => params['limit'])
);
}
AFTER:
// heroes.component.ts
// (...)
import { RouterStore } from '@ngworker/router-component-store';
@Component({
// (...)
})
export class DashboardComponent {
#routerStore = inject(RouterStore);
limit$: Observable<number> = this.#routerStore.routeParams$.pipe(
map((params) => Number(params['limit'] ?? 10))
);
}
StrictRouteData
members are now read-only
TypeScript will fail to compile application code that mutates route data data structures.
BEFORE:
// heroes.component.ts
// (...)
import { RouterStore } from '@ngworker/router-component-store';
@Component({
// (...)
})
export class DashboardComponent {
#routerStore = inject(RouterStore);
limit$: Observable<number> = this.#routerStore.routeData$.pipe(
map((data) => {
data['limit'] = Number(data['limit']);
return data;
}),
map((data) => data['limit'])
);
}
AFTER:
// heroes.component.ts
// (...)
import { RouterStore } from '@ngworker/router-component-store';
@Component({
// (...)
})
export class DashboardComponent {
#routerStore = inject(RouterStore);
limit$: Observable<number> = this.#routerStore.routeData$.pipe(
map((data) => Number(data['limit']))
);
}
RouterStore#queryParams$
and MinimalActivatedRouteSnapshot#queryParams
use StrictRouteParams
instead of Params
. Members are read-only and of type string | undefined
instead of any
TypeScript will fail to compile application code that has assumed a query parameter type other than string | undefined
.
BEFORE:
// heroes.component.ts
// (...)
import { RouterStore } from '@ngworker/router-component-store';
@Component({
// (...)
})
export class DashboardComponent {
#routerStore = inject(RouterStore);
limit$: Observable<number> = this.#routerStore.queryParams$.pipe(
map((params) => params['limit'])
);
}
AFTER:
// heroes.component.ts
// (...)
import { RouterStore } from '@ngworker/router-component-store';
@Component({
// (...)
})
export class DashboardComponent {
#routerStore = inject(RouterStore);
limit$: Observable<number> = this.#routerStore.queryParams$.pipe(
map((params) => Number(params['limit'] ?? 10))
);
}
Compatibility
To avoid compatibility issues, we now require the same RxJS peer dependency as NgRx ComponentStore, namely at least RxJS version 7.5 (#311).
- Require Angular 15.0
- Require
@ngrx/component-store
15.0 - Require RxJS 7.5
- Require TypeScript 4.8