Reputation: 433
Given a route config
{
path: '/root/:rootId',
children: [{
path: '/child1/:child1Id',
children: [{
path: '/child2/:child2Id
component: TestComponent
}]
}]
}
In TestComponent how can I easily get all route params. I'm wondering if there is an easier way than
let rootId = route.parent.parent.snapshot.params;
let child1Id = route.parent.snapshot.params;
let child2Id = route.snapshot.params;
This seems overly redundant especially if I'm watching the route params observable instead of access the param through the route snapshot. This method also seems fragile since it would break If I moved any any of the routes/params around. Im used to angular ui-router where a single object $stateParams was supplied with all param data easily accessible. I have these same concerns with route resolved data along being accessed from a single node in the route tree. Any help would be much appreciated. Thanks in advance
Upvotes: 31
Views: 28408
Reputation: 36
Based on another answers to this question, I ended up with this:
import { ActivatedRouteSnapshot, Router } from '@angular/router'
export function extractAllRouteParams(router: Router): any {
const params: any = {}
let route: ActivatedRouteSnapshot | null = router.routerState.snapshot.root
do {
Object.keys(route.params).forEach(
(key) => (params[key] = route?.params[key])
)
route = route.firstChild
} while (route)
return params
}
Put it into an shared .ts file, so you can use it anywhere, passing an instance of the router to the function, for example:
constructor(private router: Router) {}
ngOnInit(): void {
const { groupId, sectionId } = extractAllRouteParams(this.router)
}
Upvotes: 1
Reputation: 4282
If you have a use case where you need to know params from the primary outlet
in a component routed in a secondary outlet
, this function collects all params from all active outlets recursively:
const getParams = (route) => ({
...route.params,
...route.children.reduce((acc, child) =>
({ ...getParams(child), ...acc }), {})
});
getParams(router.routerState.snapshot.root);
If the current URL is /path/to/42(outlet:path/to/99)
, the resulting object could be:
{ primaryParam: '42', outletParam: '99' }
The keys in the object depend on your route configuration in Angular.
Upvotes: 2
Reputation: 8119
/**
* Get all activated route snapshot param under lazy load modules
* @see RouterModule.forChild(routes)
*/
public static getSnapshotParams(route: ActivatedRoute): { [key: string]: string } {
const params: { [key: string]: string } = {};
do {
const param = route.snapshot.params;
for (let key in param) {
params[key] = param[key];
}
route = route.parent;
} while (route);
return params;
}
/**
* Get all activated route stream observable param under lazy load modules
* @see RouterModule.forChild(routes)
* @see Observable
* @see Params
*/
public static getRouteParam(route: ActivatedRoute, routeKey: string): Observable<number> {
const obs: Observable<Params>[] = [];
do {
obs.push(route.params);
route = route.parent;
} while (route);
return merge(...obs).pipe(
filter(param => !!param[routeKey]),
map(param => param[routeKey])
);
}
}
Upvotes: 1
Reputation: 83
Angular 5:
import { combineLatest } from 'rxjs/observable/combineLatest';
constructor(
private activatedRoute: ActivatedRoute
) {
combineLatest(
this.activatedRoute.data,
this.activatedRoute.params
).subscribe(([{from}, params]) => {
this.params.from = from;
this.params.id = params['id'];
this.loadData();
});
}
Upvotes: 1
Reputation: 2389
As of Angular 5.2, you can do Router configuration to inherit all params to child states. See this commit if interested in the gory details, but here's how it's working for me:
Wherever you have your call to RouterModule.forRoot()
, include a configuration object with the inheritance strategy set to always
(default is emptyOnly
):
import {RouterModule, ExtraOptions} from "@angular/router";
export const routingConfiguration: ExtraOptions = {
paramsInheritanceStrategy: 'always'
};
export const Routing = RouterModule.forRoot(routes, routingConfiguration);
Now when you're in a child component looking at a ActivatedRoute
, ancestors' params appear there (e.g. activatedRoute.params
) rather than something messy like activatedRoute.parent.parent.parent.params
. You could access the value directly (e.g. activatedRoute.params.value.userId
) or subscribe via activatedRoute.params.subscribe(...)
.
Upvotes: 77
Reputation: 4219
I've created the following service to be able to get all route params as a ParamMap. The main idea behind it, is to recursively parse all the parameters from the child routes.
import {Injectable} from '@angular/core';
import {
ActivatedRoute,
Router,
NavigationEnd,
ParamMap,
PRIMARY_OUTLET,
RouterEvent
} from '@angular/router';
import {Observable} from 'rxjs/Observable';
import 'rxjs/add/operator/filter';
import 'rxjs/add/operator/map';
@Injectable()
export class ParamMapService {
paramMap: Observable<ParamMap>;
constructor(private router: Router,
private route: ActivatedRoute) {
this.paramMap = this.getParamMapObservable();
}
private getParamMap(route: ActivatedRoute): ParamMap {
const map: Map<string, string | string[]> = new Map();
while (route) {
route.snapshot.paramMap.keys.forEach((key) => {
map.set(key, this.getParamMapValue(route.snapshot.paramMap, key));
});
route = route.firstChild;
}
return <ParamMap>{
keys: this.getParamMapKeys(map),
has: this.getParamMapMethodHas(map),
get: this.getParamMapMethodGet(map),
getAll: this.getParamMapMethodGetAll(map)
};
}
private getParamMapMethodGet(map: Map<string, string | string[]>): (name: string) => string | null {
return (name: string): string | null => {
const value = map.get(name);
if (typeof value === 'string') {
return value;
}
if (Array.isArray(value) && value.length) {
return value[0];
}
return null;
};
}
private getParamMapMethodGetAll(map: Map<string, string | string[]>): (name: string) => string[] {
return (name: string): string[] => {
const value = map.get(name);
if (typeof value === 'string') {
return [value];
}
if (Array.isArray(value)) {
return value;
}
return [];
};
}
private getParamMapMethodHas(map: Map<string, string | string[]>): (name: string) => boolean {
return (name: string): boolean => map.has(name);
}
private getParamMapKeys(map: Map<string, string | string[]>): string[] {
return Array.from(map.keys());
}
private getParamMapObservable(): Observable<ParamMap> {
return this.router.events
.filter((event: RouterEvent) => event instanceof NavigationEnd)
.map(() => this.route)
.filter((route: ActivatedRoute) => route.outlet === PRIMARY_OUTLET)
.map((route: ActivatedRoute) => this.getParamMap(route));
}
private getParamMapValue(paramMap: ParamMap, key: string): string | string[] {
return (paramMap.getAll(key).length > 1 ? paramMap.getAll(key) : paramMap.get(key));
}
}
Example Usage
id;
constructor(private paramMapService: ParamMapService) {
this.paramMapService.paramMap.subscribe(paramMap => {
this.id = paramMap.get('id');
});
}
Note
ES6 Map is being used in the service. To support older browsers, do one of the following:
import 'core-js/es6/map';
line in polyfills.ts
, orUpvotes: 0
Reputation: 41
constructor(private route: ActivatedRoute) {}
data$ = of(this.route).pipe(
expand(route => of(route['parent'])),
takeWhile(route => !!route['parent']),
pluck('snapshot', 'data'),
reduce(merge));
Explanation:
Calling expand here creates an observable for each route in the parent chain. Takewhile is used so that it stops recursing when route['parent'] returns null, or at the root route.
Then, for all of those routes in the observable, pluck will map each to it's 'snapshot.data' property.
Finally, reduce is being fed the merge function from lodash to combine all the data objects into one object. This stream aggregates all the data from the current route through the parent routes
Upvotes: 2
Reputation: 43
I got the first route param this way:
console.log(this.route.snapshot.firstChild.params)
Upvotes: -1
Reputation: 657338
You need to iterate the route segments.
Something like
var params = [];
var route = router.routerState.snapshot.root;
do {
params.push(route.params);
route = route.firstChild;
} while(route);
This gives you the list of params
of each route segment, you then can read the param values from them that you want.
Object.keys(params)
might work to get all available param names from a param
instance.
Upvotes: 9