Skip to content
This repository was archived by the owner on Apr 12, 2024. It is now read-only.

feat($routeProvider): add support for regular expressions routes #5542

Closed
VTJamie wants to merge 3 commits into
angular:masterfrom
VTJamie:master
Closed

feat($routeProvider): add support for regular expressions routes #5542
VTJamie wants to merge 3 commits into
angular:masterfrom
VTJamie:master

Conversation

@VTJamie

@VTJamie VTJamie commented Dec 26, 2013

Copy link
Copy Markdown

I had a requirement for doing a pure regexp route with parameter grabbing. I attempted to choose a generic naming using regexp_param(n). The original path string sterilization is wrapped in an if condition to determine if it is a regular expression.

Comment thread src/ngRoute/route.js

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This could potentially be an array of values instead of individual properties. Was unsure of the prefered convention.

@IgorMinar

Copy link
Copy Markdown
Contributor

I'm sorry, but I wasn't able to verify your CLA signature. CLA signature is required for any code contributions to AngularJS.

Please sign our CLA and ensure that the CLA signature email address and the email address in this PR's commits match.

If you signed the CLA as a corporation, please let me know the company's name.

Thanks a bunch!

PS: If you signed the CLA in the past then most likely the email addresses don't match. Please sign the CLA again or update the email address in the commit of this PR.
PS2: If you are a Googler, please sign the CLA as well to simplify the CLA verification process.

@VTJamie

VTJamie commented Dec 26, 2013

Copy link
Copy Markdown
Author

Just Submitted the CLA

@IgorMinar

Copy link
Copy Markdown
Contributor

CLA signature verified! Thank you!

Someone from the team will now triage your PR and it will be processed based on the determined priority (doc updates and fixes with tests are prioritized over other changes).

@ghost ghost assigned tbosch Dec 27, 2013
@tbosch

tbosch commented Dec 27, 2013

Copy link
Copy Markdown
Contributor

Hi,
could you also add unit tests for this and an example?

Thanks!

Jamieson Abbott added 2 commits December 27, 2013 22:53
Comment thread test/ngRoute/routeSpec.js

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added Unit test to verify that standard routes, routes with params, regex routes without params, and regex routes with params work

@VTJamie

VTJamie commented Dec 28, 2013

Copy link
Copy Markdown
Author

Example use.

$routeProvider.when('/route', {controller: angular.noop, reloadOnSearch: false}); //Regular route
$routeProvider.when(/^/home$/, {controller: angular.noop, reloadOnSearch: false}); //Regex Route no params
$routeProvider.when(/^/home/([0-9]+)$/, {controller: angular.noop, reloadOnSearch: false}); //Regex route with one param
$routeProvider.when('/route/:abc', {controller: angular.noop, reloadOnSearch: false}); //Regular route with one param

@caitp

caitp commented Dec 28, 2013

Copy link
Copy Markdown
Contributor

I'm wondering if it might better to do something more like ui-router's url format documented here, where parameters can be in a regexp format if necessary. I can see that being helpful to people, but I'm not totally sure I understand the net benefit of using a regexp for a route url.

@VTJamie

VTJamie commented Dec 28, 2013

Copy link
Copy Markdown
Author

This is an attempt to provide the most power possible, with the smallest change possible. It is also designed to be a completely take it or leave it feature. Also without needing to change the developer's current implementation of $routeProvider over to $stateProvider.

@litera

litera commented Jan 22, 2014

Copy link
Copy Markdown

@caitp : I implemented something similar to what you want.
It works this way:

/some/route/:parameter:regexp/other

So you can define things like:

/profile/:id:[1-9]\\d*/:name?
/books/:isbn10:\\d{10}
/books/:isbn13:\\d{3}-\\d{10}

Which will only match those routes where id is actually a number (first example). Double backslash is required because Javascript would otherwise use it as an escape character and will get stripped out as "d" doesn't need escaping in normal Javascript strings.

Important: Even though that regular expression in first case ends with * it doesn't mean that this route parameter is greedy (see conditions below). This character is treated as part of provided regular expression.

What were the changes that I did? I'm not making a pull request for this as it seem too tedious, but it's a simple code addition to route.js file

Replace these lines:

path = path
  .replace(/([().])/g, '\\$1')
  .replace(/(\/)?:(\w+)([\?\*])?/g, function(_, slash, key, option){
    var optional = option === '?' ? option : null;
    var star = option === '*' ? option : null;
    keys.push({ name: key, optional: !!optional });
    slash = slash || '';
    return ''
      + (optional ? '' : slash)
      + '(?:'
      + (optional ? slash : '')
      + (star && '(.+?)' || '([^/]+)')
      + (optional || '')
      + ')'
      + (optional || '');
  })
  .replace(/([\/$\*])/g, '\\$1');

with these:

path = path
  .replace(/([().])/g, '\\$1')
  .replace(/(\/)?:(\w+)(?::([^\/]+))?([?*])?/g, function (_, slash, key, rx, option) {
    var optional = option === '?' ? option : null;
    var star = option === '*' ? option : null;
    keys.push({ name: key, optional: !!optional });
    slash = slash || '';
    return ''
      + (optional ? '' : slash)
      + '(?:'
      + (optional ? slash : '')
      + (star && '(.+?)' || rx && ('(' + rx + ')') || '([^/]+)')
      + (optional || '')
      + ')'
      + (optional || '');
  })
  .replace(/([\/$\*])/g, '\\$1');

All the usual stuff applies:

  1. regular expression can't include forward slashes as that's route segment delimiter
  2. regular expression can't include capturing groups as those would break parsing later on; "(" and ")" characters will be escaped as they're already being escaped
  3. route parameters with regular expressions can't be optional or greedy

@NotBobTheBuilder

Copy link
Copy Markdown
Contributor

Any update on this? I'd really like to see it merged.

@IgorMinar

Copy link
Copy Markdown
Contributor

-1

params in the format regexp_paramN or an array of values are not acceptable solution.

@VTJamie you haven't explained in what real world scenarios would this feature be useful. I can think of some exotic corner cases, in which I'd rather rethink the route patterns than try to create complex regular expressions to match them.

@litera's implementation is more interesting and he provides the example of parsing the route param into isbn10 or isbn13 depending on the pattern. But even in this case, I'd move the responsibility of determining the isbn format to the controller rather than the router.

@btford @EisenbergEffect what's your take on defining routes via regexps instead of pattern strings?

@IgorMinar IgorMinar modified the milestones: Purgatory, Backlog Jul 22, 2014
@NotBobTheBuilder

Copy link
Copy Markdown
Contributor

As an example, consider a case of browsing through a collection of collections, for example a website of photo galleries. The site may have the URL structure

/galleries/:gallery/photos/:photo

but in going to

/galleries/:gallery/

you just start off at the most recently added photo. Without the literal string photos in the path, angular could model that already with /galleries/:gallery/:photo?, but it's not hard to imagine a constraint that requires photos to be present (perhaps the gallery has comments too, for example).

That kind of thing would be trivial with a RegEx, looking something like /galleries/(.*)/(photos/(.*))?, off the top of my head.

@NotBobTheBuilder

Copy link
Copy Markdown
Contributor

(That's not to say this is the right PR to add that feature, only that I think this feature is worth having)

@EisenbergEffect

Copy link
Copy Markdown

Well, I'm not personally a fan of RexEx. So, I've got a bias here. I feel that they introduce a bit of a maintenance issue since RegEx can tend to be "write only" code. It takes considerably more mental effort for a developer to process what a RegEx is doing if they didn't just finish writing it ;) That said, it's important not to block developers from getting their job done, so if there's a legitimate scenario that cannot be modeled by the simple route syntax, then I think it's worth it to try to find some solution.

I'm much less familiar with the 1.x router, so I won't speak on that, but I can talk a bit about how this relates to the 2.0 router we've been working on, for those who are interested.

First, we're currently using the RouteRecognizer library to do our pattern matching. It's a solid library with a consistent set of behavior. A side effect of this library is that it does not represent route patterns via RegEx internally. Rather, it builds up its own state machine for matching. So, our first barrier (well, not really, as you'll see below) in 2.0 is that our underlying matcher doesn't support raw RegEx. It does support wildcard routes, which I think could be used in some fashion to solve this problem though.

However, the 2.0 router is very flexible and it supports an "unknown route handler". Whenever a route is not matched via the standard mechanism above, we look to see if you've registered this custom handler. If you have, we pass you the raw route data and expect you to build up what we call a NavigationInstruction. You can build this instruction up any way you like, including RegEx. You can even asynchronously call to the server to get the data you need. When you are finished, you hand it back to us and the Router uses it to process the navigation. So, it is at this level that one could plug in any RegEx strategy that they desired and teach the router how to handle that.

I'm aware that the solution for the 2.0 router I present here isn't as simple as declaring a RegEx route. But I think it's important to make the 80%-90% use case dead simple and the other cases "possible". So, I believe we have achieved that. I should also make another note on why it's built this way. The 2.0 router supports nested child routers and sibling routers. We also support a lot of internal smarts that determine when/how to transition state in the context of these hierarchical route scenarios. As a result, it's important that the route info is not a black box. We usually need to look at that and determine other information during the course of routing. Supporting arbitrary RegEx would make this very difficult. This is why we actually give you the hook to generate a NavigationInstruction, as mentioned above. The instruction provides a query surface for the router's internal pipeline that allows it to gain access to the information it needs to transition between states. As a developer, having knowledge of your own RegEx, you can then provide us the information we require.

Finally, since I'm talking about 2.0 here....none of this is set in stone. You can take a look at our current implementation here. It's still far from complete, but there's enough there for you to get a pretty solid feel for where we are going with this. I always welcome feedback...and if you've got some cool ideas about supporting RegEx in that implementation, I'd love to hear them.

@caitp

caitp commented Jan 6, 2015

Copy link
Copy Markdown
Contributor

sooooo, it's been a long time since this was proposed, and it sounds like Rob and Igor weren't keen on it, plus it's rather bitrotten now.

@btford maybe it's something you might want to look at for the new router, but we should definitely try to keep things as simple as possible imho.

Closing this for now, sorry :(

@caitp caitp closed this Jan 6, 2015
@NotBobTheBuilder

Copy link
Copy Markdown
Contributor

No worries :)

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

8 participants