HiveBrain v1.2.0
Get Started
← Back to all entries
patternjavascriptMinor

Simple JavaScript router with params

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
paramssimplejavascriptwithrouter

Problem

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?

Solution

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-]+)') + '

That's all I can think of.; 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.

Code Snippets

// 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);
}

Context

StackExchange Code Review Q#117596, answer score: 3

Revisions (0)

No revisions yet.