Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Angular UI-Router more Optional Parameters in one State

How can I allow optional parameters to my routes without using a query string and only using one route name? I am currently specifying each route FIVE TIMES to allow for any combination of parts:

All parts must be optional. Route must resolve any variation.

.state("login", { url: "/login", templateUrl: "login.html", params: { a: null, b: null, c: null, d: null } })
.state("loginA", { url: "/login/:a", templateUrl: "login.html", params: { b: null, c: null, d: null } })
.state("loginAB", { url: "/login/:a/:b", templateUrl: "login.html", params: { c: null, d: null } })
.state("loginABC", { url: "/login/:a/:b/:c", templateUrl: "login.html", params: { d: null } })
.state("loginABCD", { url: "/login/:a/:b/:c/:d", templateUrl: "login.html" })

There MUST be an easier / cleaner / less ugly way.

like image 379
G. Deward Avatar asked May 13 '15 21:05

G. Deward


People also ask

How do you make a route parameter optional?

You can make a URI parameter as optional by adding a question mark (“?”) to the route parameter. If you make a route parameter as optional then you must specify a default value by using parameter = value for the method parameter.

What is $state in AngularJS?

$stateProvider is used to define different states of one route. You can give the state a name, different controller, different view without having to use a direct href to a route. There are different methods that use the concept of $stateprovider in AngularJS.

What does UI sref do?

A ui-sref is a directive, and behaves similar to an html href . Instead of referencing a url like an href , it references a state. The ui-sref directive automatically builds a href attribute for you ( <a href=...> </a> ) based on your state's url.

What is state URL?

A state's URL is actually a URL fragment. Each state defines only the fragment (portion) of the URL that it “owns”. That fragment is appended to the parent state's url in the browser URL when the nested state is active.


3 Answers

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 },
    }
})
like image 116
Fred Lackey Avatar answered Nov 11 '22 08:11

Fred Lackey


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

like image 26
Radim Köhler Avatar answered Nov 11 '22 10:11

Radim Köhler


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.

like image 3
Nick A. Watts Avatar answered Nov 11 '22 10:11

Nick A. Watts