I am building a JS router and would like to have some help doing it. It will work like so:
A list of routes is given with a URL pattern attached in each one.
[
{ url : "/home"},
{ url : "/user/:id"}
]
I want a function that will find the appropriate pattern when we pass it a route URL and will return the object of parameters:
getRouteParams("/home"); // {params: {}}
getRouteParams("/user/42"); // {params: {id: "42"}}
getRouteParams("/contact"); // null
I tried to do something with some parts of code I have found on the web and here is what I ended up with:
var routes = [
{ url : "/home"},
{ url : "/user/:id"}
];
function getRouteParams(url) {
var argsVal = null;
var x = 0;
var result = {};
for(; routes.length; x++){
var routeMatcher = new RegExp(routes[x].url.replace(/(:\w+)/g, '([\\w-]+)'));
argsVal = url.match(routeMatcher);
if(argsVal) {
argsVal.shift();
result.params = makeObj(argsVal, routes[x].url);
break;
}
}
return result;
function makeObj(vals, url) {
var routeParts = url.split('/');
var options = {};
for(var i=0, j=0; i<routeParts.length; i++) {
if(routeParts[i].indexOf(":") !== -1) {
options[routeParts[i].slice(1)] = vals[j++];
}
}
return options;
}
}
console.log(getRouteParams("/home")); // {params: {}}
console.log(getRouteParams("/user/42")); // {params: {id: "42"}}
console.log(getRouteParams("/contact")); // null)
This is a bit more sophisticated but the idea is there. Could someone help me improve on what I did?
-
1\$\begingroup\$ I edited my post, sorry for that, this is my first post... \$\endgroup\$ZeZeN– ZeZeN2016年01月22日 14:25:53 +00:00Commented Jan 22, 2016 at 14:25
-
2\$\begingroup\$ Welcome to Code Review! I have rolled back the last edit. Please see what you may and may not do after receiving answers . \$\endgroup\$Quill– Quill2016年01月25日 09:20:56 +00:00Commented Jan 25, 2016 at 9:20
-
\$\begingroup\$ I posted a self response instead, hope this is what was expected... \$\endgroup\$ZeZeN– ZeZeN2016年01月25日 09:45:39 +00:00Commented Jan 25, 2016 at 9:45
2 Answers 2
Your algorithm seems to be working.
Here is the few things I would do to improve it:
// The routes should be a parameter of the function
function getParams(routes, url) {
var params = null;
// Depending on the targeted browser, you could use the native forEach method
routes.forEach(function (route) {
// I personally prefer to use methods in here
if (params === null && routeMatch(route.url, url)) {
params = computeParams(route.url, url);
}
});
return { params: params };
function routeMatch(route, url) {
// You don't need to use `new RegExp`
// I've added ^ and $ to be sure that it would be working with nested routes
var matcher = '^' + route.replace(/(:\w+)/g, '([\\w-]+)') + '$';
return url.match(matcher);
}
function computeParams(route, url) {
var routeParts = route.split('/');
var urlParts = url.split('/');
var options = {};
for (var i = 0, nbOfParts = routeParts.length; i < nbOfParts; i++) {
// I check that the url part exists
// and use the binary operator ~ that simplifies the `indexOf` method
if (urlParts[i] && ~routeParts[i].indexOf(':')) {
options[routeParts[i].slice(1)] = urlParts[i];
}
}
return options;
}
}
var routes = [ { url: '/home' }, { url: '/user/:id' }, { url: '/user/:id/post/:postId' }, { url: '/user/:id/post/:postId/comment/:cId' } ];
printResult(getParams(routes, "/home").params, {});
printResult(getParams(routes, "/user/42").params, { id: '42' });
printResult(getParams(routes, "/user/42/post/52").params, { id: '42', postId: '52' });
printResult(getParams(routes, "/user/42/post/52/").params, null);
printResult(getParams(routes, "/user/42/post/52/comment/").params, null);
printResult(getParams(routes, "/user/42/post/52/comment/yolo-22").params, { id: '42', postId: '52', cId: 'yolo-22' });
printResult(getParams(routes, "/contact").params, {});
function printResult(result, expected) {
console.log('expected: ', expected, 'result: ', result);
}
That's all I can think of.
-
\$\begingroup\$ Thank you!! I'll study your answere and try to enhance what I've made before your post. \$\endgroup\$ZeZeN– ZeZeN2016年01月25日 09:14:38 +00:00Commented Jan 25, 2016 at 9:14
I improved my code in the meanwhile, before looking at what you suggested. I'll study how I could merge that with yours.
Here is my actual improved code :
var routes = [
{url: "/users/:uid/pictures/:avatar"},
{url: "/users/:uid"},
{url: "/home"}
];
function getRouteParams(url) {
var argsVal,
argsNames,
params = {};
for(var x = 0; x < routes.length; x++){
var currRoute = routes[x].url;
var routeMatcher = new RegExp(currRoute.replace(/(:\w+)/g, '([\\w-]+)'));
argsVal = url.match(routeMatcher);
if(argsVal) {
argsVal.shift();
argsNames = currRoute.match(/(:\w+)/g);
if(argsNames) {
for(var y = 0; y < argsNames.length; y++){
params[argsNames[y].slice(1)] = argsVal[y];
}
}
return {
params : params
};
}
}
return null;
}
console.log(getRouteParams("/users/1024/pictures/ok")); // {params:{uid:"1024", avatar: "ok"}
console.log(getRouteParams("/users/zezen")); // {params: {uid: "zezen"}}
console.log(getRouteParams("/home")); // {params: {}}
console.log(getRouteParams("/zezen")); // null