February 23, 2015

Defining optional parameters with AngularJS UI Router

I’m currently working on the new dashboard for Fabrik. In Fabrik you use Portfolios to organise your Projects. It’s possible to create Projects directly and then assign them to Portfolios, or you can create a Project from a Portfolio, assigning it automatically.

In the latter scenario we need to pass the relevant Portfolio Id to our Projects controller. There are a number of ways optional parameters can be declared when using UI Router.

Query Parameters

So that query parameters are mapped to UI Router’s $stateParams object, you need to declare them in your state configuration’s URL template:

state('new-qs', {
  url: '/new?portfolioId',
  templateUrl: 'new.html',
  controller: function($scope, $stateParams) {
     $scope.portfolioId = $stateParams.portfolioId;
  }
})

You can then create a link to this state using the ui-sref attribute:

<a ui-sref="new-qs({ portfolioId: 1 })">New (query string)</a>

This will navigate to /new?portfolioId=1.

If you have multiple optional parameters, separate them with an &:

state('new-qs', {
  url: '/new?portfolioId&param1&param2',
  templateUrl: 'new.html',
  controller: function($scope, $stateParams) {
     $scope.portfolioId = $stateParams.portfolioId;
     $scope.param1 = $stateParams.param1;
     $scope.param2 = $stateParams.param2;
  }
})

Optional Route Parameters

Route parameters in UI Router are optional by default. This means that in the example below both /new/2 and /new/ would work, but not /new (no trailing slash). The ? suffix you often see on route parameters simply tells UI Router where the query portion of the URL template should start e.g. /new/:portfolioId?.

state('new-rp', {
  url: '/new/:portfolioId',
  templateUrl: 'new.html',
  controller: function($scope, $stateParams) {
     $scope.portfolioId = $stateParams.portfolioId;
  }
})

Updating the ui-sref attribute to new-rp({ portfolioId: 2 }) will produce the link /new/2.

If you’re not a fan of that trailing slash then you’ll need to set up another route for the parameterless version.

For multiple optional route parameters e.g. /posts/tagged/:tag/page/:page it looks like the only solution right now is to declare multiple routes i.e. /posts/tagged/:tag and /posts/tagged/:tag/page/:page.

### Non-URL route parameters

Another option that I discovered recently is how to pass parameters that do not appear in the URL. This is particularly useful for abstract states.

state('new-nonurl', {
  url: '/new',
  params: {
    portfolioId: null,
  },
  templateUrl: 'new.html',
  controller: function($scope, $stateParams) {
     $scope.portfolioId = $stateParams.portfolioId;
  }
})

You can also use the params object to declare default values for your route parameters.

Now ui-sref="new-nonurl({ portfolioId: 3 })" will generate the link \new but still pass the portfolioId parameter in $stateParams.

View the plunk demonstrating all 3 techniques here.

© 2022 Ben Foster