Suppose we have these kinds of urls:
[1] /home/users/:id
[2] /home/users/:id/posts/:id
[3] /home/users/:id/posts/:id/comments/:id
I want to make a method parseUrl(url: string): any[] {} which takes a url and returns an array containing each and every parameters of that url. So, parseUrl('/home/users/:id/posts/:id/comments/:id')
will result in [userId, postId, commentId]
How can I achieve that?
Naive approach:
Suppose we know which url segments can contain parameters (in this case, users, posts and comments), we can split the url by '/'
character, the check if the segment is equal users or posts or comments, and finally take the subsequent segment as url.
parseUrl(url: string): any[] {
let params = new Array<any>();
url.split('/')
.filter((segment, index, urlSegments) => index > 0 && ['users', 'posts', 'comments'].includes(urlSegments[index - 1]))
.forEach(segment => params.push(segment))
return params;
}
Why this sucks? --> it's actually highly coupled to the structure of the url. If for example I had also a url like this: /home/users/new-post
, then new-post
would be deemed as a parameter.
Using ActivatedRoute
or ActivatedRouteSnapshot
: using the params property of *ActivatedRouteSnapshot` seems better because it's free from the structure of our url. The problem here is that in my particular case, I am able to retrieve the parameter of just the last url segment. So
parseUrl(route: ActivatedRouteSnapshot) {
return route.params;
}
will result in an object containing just for example {'id': 5}
give /home/users/10/posts/10/comments/5
as current url. This is because (at least I think) I've configured lazy loaded module for users, posts and comments. So I have 3 routing module, one which matches the route users/:id
, the second matches posts/:id
and the third matches comments/:id
. I've found ActivatedRouteSnapshot treating them as 3 separated url segments with one parameter each instead of a one single url with 3 parameters.
So in the end, is there a programmatic and general way to get each single parameter from an url in Anuglar 9?
You would need to recursively walk the router tree to collect all params. This snippet works only, if your paramKeys are unique, so
/home/users/:id/posts/:id/comments/:id
would need to be /home/users/:userId/posts/:postId/comments/:commentId
. If you would want to keep the old paramkey names, you would need to adapt the snippet accordingly.
It could look like this:
export parseUrl(route: ActivatedRouteSnapshot): Map<string,string> {
const result = reduceRouterChildrenParams(route.root.firstChild, new Map<string, string>());
return result;
}
reduceRouterChildrenParams(routerChild: ActivatedRouteSnapshot | null, data: Map<string, string>): RouteWalkerResult {
if (!routerChild) {
return data;
}
for (const paramMapKey of routerChild.paramMap.keys) {
data.set(paramMapKey, routerChild.paramMap.get(paramMapKey)!);
}
return routerChild.children.reduce((previousValue, currentValue) => {
return reduceRouterChildrenParams(currentValue, previousValue);
}, data);
}