Skip to content
Tim Kindberg edited this page Aug 14, 2013 · 47 revisions

◄ Back (Multiple Named Views)     Next (The Components) ►

Most states in your application will probably have a url associated with them. URL Routing was not an after thought to the state mechanics, but was figured into the design from the beginning (all while keeping states separate from url routing)

Here's how you set a basic url.

$stateProvider
    .state('contacts', {
        url: "/contacts",
        templateUrl: 'contacts.html'
    })

Now when the user accesses index.html/contacts then the 'contacts' state would become active and the main ui-view will be populated with the 'contacts.html' partial. Alternatively, if the user were to transition to the 'contacts' state via transitionTo('contacts') then the url would be updated to index.html/contacts

URL Parameters

Basic Parameters

Often, URLs have dynamic parts to them which are called parameters. There are several options for specifying parameters. A basic parameter looks like this:

$stateProvider
    .state('contacts.detail', {
        url: "/contacts/:contactId",
        templateUrl: 'contacts.detail.html',
        controller: function ($stateParams) {
            // If we got here from a url of /contacts/42
            expect($stateParams).toBe({contactId: 42});
        }
    })

Alternatively you can also use curly brackets:

// identical to previous example
url: "/contacts/{contactId}" 

Examples:

  • '/hello/' - Matches only if the path is exactly '/hello/'. There is no special treatment for trailing slashes, and patterns have to match the entire path, not just a prefix.
  • '/user/:id' - Matches '/user/bob' or '/user/1234!!!' or even '/user/' but not '/user' or '/user/bob/details'. The second path segment will be captured as the parameter 'id'.
  • '/user/{id}' - Same as the previous example, but using curly brace syntax.

Regex Parameters

A bonus to using curly brackets is the ability to set a Regular Expression rule for the parameter:

// will only match a contactId of one to eight number characters
url: "/contacts/{contactId:[0-9]{1,8}}"

Examples:

  • '/user/{id:[^/]*}' - Same as '/user/{id}' from the previous example.
  • '/user/{id:[0-9a-fA-F]{1,8}}' - Similar to the previous example, but only matches if the id parameter consists of 1 to 8 hex digits.
  • '/files/{path:.*}' - Matches any URL starting with '/files/' and captures the rest of the path into the parameter 'path'.
  • '/files/*path' - Ditto. Special syntax for catch all.

Warning: Don't put capturing parentheses into your regex patterns, the UrlMatcher in ui-router adds those itself around the entire regex. You're effectively introducing a second capture group for the same parameter, which trips up the numbering in the child URL. You can use non-capturing groups though, i.e. (?:...) is fine.

Query Parameters

You can also specify parameters as query parameters, following a '?':

url: "/contacts?myParam"
// will match to url of "/contacts?myParam=value"

If you need to have more than one, separate them with an '&':

url: "/contacts?myParam1&myParam2"
// will match to url of "/contacts?myParam1=value1&myParam2=wowcool"

URL Routing for Nested States

Appended Routes (default)

When using url routing together with nested states the default behavior is for child states to append their url to the urls of each of its parent states.

$stateProvider
  .state('contacts', {
     url: '/contacts',
     ...
  })
  .state('contacts.list', {
     url: '/list',
     ...
  });

So the routes would become:

  • 'contacts' state matches "/contacts"
  • 'contacts.list' state matches "/contacts/list". The urls were combined.

Absolute Routes (^)

If you want to have absolute url matching, then you need to prefix your url string with a special symbol '^'.

$stateProvider
  .state('contacts', {
     url: '/contacts',
     ...
  })
  .state('contacts.list', {
     url: '^/list',
     ...
  });

So the routes would become:

  • 'contacts' state matches "/contacts"
  • 'contacts.list' state matches "/list". The urls were not combined because ^ was used.

$stateParams Service

As you saw previously the $stateParams service is an object that will have one key per url parameter. The $stateParams is a perfect way to provide your controllers or other services with the individual parts of the navigated url.

// If you had a url on your state of:
url: '/users/:id/details/{type}/{repeat:[0-9]+}?from&to'

// Then you navigated your browser to:
'/users/123/details//0'

// Your $stateParams object would be
{ id:'123', type:'', repeat:'0' }

// Then you navigated your browser to:
'/users/123/details/default/0?from=there&to=here'

// Your $stateParams object would be
{ id:'123', type:'default', repeat:'0', from='there', to='here' }

$urlRouterProvider

$urlRouterProvider has the responsibility of watching $location. When $location changes it runs through a list of rules one by one until a match is found. $urlRouterProvider is used behind the scenes anytime you specify a url in a state configuration. All urls are compiled into a UrlMatcher object (see $urlMatcherFactory below).

There are several methods on $urlRouterProvider that make it useful to use directly in your module config.

when() for redirection

Parameters:

  • match String | RegExp | UrlMatcher The incoming path that you want to redirect.
  • handler String | Function The path you want to redirect your user to. If handler is a string, it is treated as a redirect, and is interpolated according to the syntax of match (i.e. like String.replace() for RegExp, or like a UrlMatcher pattern otherwise). If the handler is a function, it gets invoked if $location matches. It can return:
    • falsy to indicate that the rule didn't match after all, then $urlRouter will continue trying to find another one that matches.
    • a String, which is treated as a redirect and passed to $location.url()
    • nothing or any truthy value tells $urlRouter that the url was handled
app.config(function($urlRouterProvider){
    // when there is no route, redirect to /index
    
    $urlRouterProvider.when('', '/index');

    // Tip: you can also use a regexp for the first parameter. No example shown.
})

otherwise() for invalid routes

Parameters:

  • path String The path you want to redirect to
app.config(function($urlRouterProvider){
    // if the path doesn't match any of the urls you configured
    // otherwise will take care of routing the user to the specified url
    $urlRouterProvider.otherwise('/index');
})

rule() for custom url handling

Parameters:

  • handler Function A function that takes in the $injector and $location services as arguments. You are responsible for returning a valid path as a string.
app.config(function($urlRouterProvider){
    // Here's an example of how you might allow case insensitive urls
    $urlRouterProvider.rule(function ($injector, $location) {
        var path = $location.path(), normalized = path.toLowerCase();
        if (path != normalized) return normalized;
    });
})

$urlMatcherFactory and UrlMatchers

Defines the syntax for url patterns and parameter placeholders. This factory service is used behind the scenes by $urlRouterProvider to cache compiled UrlMatcher objects, instead of having to re-parse url patterns on every location change. Most users will not need to use $urlMatcherFactory directly, however it could be useful to craft a UrlMatcher object and pass it as the url to the state config.

Please refer to the comment documentation within the $urlMatcherFactory file to learn more.

var urlMatcher = $urlMatcherFactory.compile("/home/:id?param1");
$stateProvider.state('myState', {
    url: urlMatcher 
});

◄ Back (Multiple Named Views)     Next (The Components) ►