Angular UI-Router more Optional Parameters in one State

Short answer....

.state('login', {
    url: '/login/:a/:b/:c/:d',
    templateUrl: 'views/login.html',
    controller: 'LoginCtrl',
    params: {
        a: { squash: true, value: null },
        b: { squash: true, value: null },
        c: { squash: true, value: null },
        d: { squash: true, value: null },
    }
})

There is a working plunker

Solution here could be of two types. The first is really very dynamic. The second is working as needed, a bit more rigid, but profiting from UI-Router built-in features.

I. Dynamic approach

Let's observe the first, which is interesting (but maybe too much complicated in our scenario). It is very similar to this Q & A

Recursive ui router nested views

We try to solve the url which contains unknown amount of folders*(directories)* names:

<a href="#/files/Folder1">
<a href="#/files/Folder1/SubFolder1/">
<a href="#/files/Folder1/SubFolder1/SubFolderA">

State could be define like this:

.state('files', {
  url: '/files/{folderPath:[a-zA-Z0-9/]*}',
  templateUrl: 'tpl.files.html',
  ...

And that will lead to one param folderPath with the complete folder path.

In case we would like to solve our scenario (handle exactly three params) we could extend stuff like this

Controller for File handling:

.controller('FileCtrl', function($scope, a, b, c) {
    $scope.paramA = a; 
    $scope.paramB = b; 
    $scope.paramC = c; 
})

State definition using resolver:

// helper method
var findParams = function($stateParams, position) {
   var parts = $stateParams.folderPath.split('/')
   var result = parts.length >= position ? parts[position] : null;
   return result;
  }

...

// state calls resolver to parse params and pass them into controller
.state('files', {
    url: '/files/{folderPath:[a-zA-Z0-9/]*}',
    templateUrl: 'tpl.files.html',
    controller: 'FileCtrl',
    resolve: {
        a : ['$stateParams', function($stateParams) {return findParams($stateParams, 0)}],
        b : ['$stateParams', function($stateParams) {return findParams($stateParams, 1)}],
        c : ['$stateParams', function($stateParams) {return findParams($stateParams, 2)}],
    }
 })

II. UI-Router built-in feature params : {}

The second scenario, is in fact very very simple. It uses UI-Router built in feature: params : {}. Check its documentation here:

http://angular-ui.github.io/ui-router/site/#/api/ui.router.state.$stateProvider

This would be our state definition:

.state('login', {
    url: '/login/:a/:b/:c',
    templateUrl: 'tpl.login.html',
    controller: 'LoginCtrl',
    params: {
        a: {squash: true, value: null},
        b: {squash: true, value: null},
        c: {squash: true, value: null},
    }
})

And all these links will work as well:

<a href="#/login">
<a href="#/login/ValueA">
<a href="#/login/ValueA/ValueB">
<a href="#/login/ValueA/ValueB/ValueC">

And what was the trick:

squash - {bool|string=}: squash configures how a default parameter value is represented in the URL when the current parameter value is the same as the default value. If squash is not set, it uses the configured default squash policy.

Check it in action here


Another simple way to do this is to just set a default value for the parameter, like this:

params: {
  thing1: ""
}

According to the Angular UI Router docs, setting a default value automatically makes the parameter optional.