baseprime / grapnel

The smallest JavaScript router with named parameters, HTML5 pushState, and middleware support
http://grapnel.js.org
467 stars 40 forks source link

Support for optional wildcard in routes #33

Closed asabhaney closed 9 years ago

asabhaney commented 9 years ago

As of right now, it seems the following route syntax does not work:

router.get('settings/*?', handler)

Since there is currently support for optional parameters, as well as wildcard parameters, it would be awesome if this plugin could support optional wildcards. I know this can already be accomplished with a RegEx, but using the nicer syntax would be that much better.

This would be particularly useful if you wanted to handle a bunch of routes with a common context (eg. /settings, /settings/general, /settings/users/add) with one event handler.

baseprime commented 9 years ago

What version are you using? The route syntax you provided works as far as I know:

var router = new Grapnel();

router.get('/settings/*?', function(req){
    console.log(req.params[0]);
});

router.navigate('/settings/works');
// => works
asabhaney commented 9 years ago

Ah sorry, the issue seems to be a specific edge case. The only URL that doesn't work is when there is no trailing slash: /settings.

Changing the route to the following solves the issue:

router.get('/settings/?*?', handler)

If this is the way the route matching was intended to work, we can go ahead and close the issue.

Using version 0.5.8.

asabhaney commented 9 years ago

Just noticed that my solution above does not work in all cases.

router.get('/settings/:entity/?*?', function(req) {
    console.log(req.params.entity);
});

This doesn't seem to work properly if, for example, the URL is /settings/users/test. The route seems to match, but in the route handler, req.params.entity is "u".

baseprime commented 9 years ago

Just curious, why are you using two quantifiers ? in the route path? They both resolve to RegExp, so what you're actually doing is and defining a route that contains a question mark and then separating the previous capture. They are not required for matching items with a wildcard.

router.get('/settings/:entity/*', function(req){
    console.log(req.params.entity, req.params[1]);
});

Why not just use RegExp?

var router = new Grapnel(),
    regexp = /^\/settings\/(?:([^\/]+?))(?:\/([^\/]+?))?\/?$/i;

router.get(regexp, function(req, event){
    var params = regexp.exec(event.value).slice(1),
        entity = params[0],
        area = params[1];

    console.log(entity, area);
});
asabhaney commented 9 years ago

Thanks for the response. Yeah this is not really a big deal, because you're right - I could just use a RegExp. But it's just nicer to be able to use the named parameters syntax =)

I would like to be able to use one route handler for all of the following matching URLs, and if possible I'd like to be able to take advantage of the named parameters.

/settings
/settings/user
/settings/user/test

However I cannot accomplish that with

router.get('/settings/:entity/*', handler)

nor with

router.get('/settings/:entity/*?', handler)

Forgive me if I am trying to make the router work in a way that it was not intended to work!

baseprime commented 9 years ago

You can use named parameters on more than one level.

router.get('/settings/:entity?/:area?', function(req){
    console.log(req.params.entity, req.params.area);
});
asabhaney commented 9 years ago

Right, I was thinking more in the off chance that you didn't know what the parameters are or how many there are.