Multiple optional parameters in Angular.js - angularjs

I typically use querystrings for filtering results, eg:
URL: /Events/List?type=art&city=miami&life=present
I can put them in any order and get the same results, eg:
URL: /Events/List?city=miami&life=present&type=art
URL: /Events/List?life=present&type=art&city=miami
I can also make them optional, eg:
URL: /Events/List?type=art,
or: /Events/List?type=art&life=&city=
Question: How can I achieve this same "effect" with Angular.js routes? given the fact that parameters are passed all in a "RESTful" way in Angular
I was thinking in something like this:
URL: /Events/List/city/miami/life/present/type/art
Which I can achieve with a route like this:
.when('/Events/List/city/:city?/life/:life?/type/:type?', { ... }
But I already see many problems:
Order of parameters is important (I would have to define many times the route?)
Optional parameters will give a non intuitive URL,
eg: /Events/List/city/life/type/art,
and this is not the case in optional querystrings (they are more intuitive to read I think): eg: /Events/List/?city=&life=&type=art

If you want to use query strings angular has $location.search() that is both a setter and getter.
The difference between angular search and window version location.search is the query falls after the url hash and when setting or getting it uses objects rather than strings
See Using $location in developer's guide

You can inject $routeParams into your controller. Here's an example from the docs:
// Given:
// URL: http://server.com/index.html#/Chapter/1/Section/2?search=moby
// Route: /Chapter/:chapterId/Section/:sectionId
//
// Then
$routeParams ==> {chapterId:1, sectionId:2, search:'moby'}
http://docs.angularjs.org/api/ngRoute.$routeParams

I recently encountered a similar need (the ability to grab an unknown quantity of arguments off the path), and had started off by looking at $routeProvider. I ultimately abandoned that effort in favor of ui.router.
The most powerful (but possibly also tedious) approach for you would be to work with regex parameters. Consider the following (a snippet from some code I'm currently authoring):
$stateProvider
.state("list", {
url: "/",
controller: "PluginsListController",
templateUrl: "PluginsList.html"
})
.state("path", {
url: "/{plugin}/*path",
controller: "PluginDetailsController",
templateUrl: "PluginDetails.html"
})
;
The second state accepts two positional parameters: a plugin and a path. The path argument is a wildcard, that grabs everything after the immediately preceding slash. The idea here is that I can specify something like http://www.myfirsturl.com/app/MyFirstPlugin/level1/level2/level2.php, and end up with "MyFirstPlugin" in $stateParams["plugin"] and "level1/level2/level2.php" in $stateParams["path"]. It's up to the application logic to handle the long path parameter (and you would be likewise responsible for consuming this variable-length argument), but this approach does allow you to write a single state handler for an unknown number of url paths.

Related

Multiple optional route parameters and pretty urls with Angular ui router

I am trying to use multiple optional parameters to make urls looks like
/:region/:direction/:subdirection/page/
where all params instead of the last one are optional.
I've tried to use
params:{region: {value: 'default', squash:true}, ...}
for each optional parameter, but when some of them are missed in url, the router doesn't work.
So, the only solution I've found in order to save pretty urls in this situation is to declare multiple routes:
.state('page', {
url: '/:region/page'
}
.state('page-direction', {
url: '/:region/:direction/page'
}
.state('page-subdirection', {
url: '/:region/:direction/:subdirection/page'
}
Additionally:
1. there will be no subdirection without direction,
2. region will be in every link, but it is variable
Are there any optional solutions?
Thnx!
The default parameters are for internal routing, example
$state.go('page-subdirection');
The above will redirect to the state and the $stateParams.region will be the default value you defined for that state.
While $state.go('page-subdirection', {region: 'newValue'); will make $stateParams.region to be newValue in that specific case.
The same apply for constructing the URL from the view:
<a ui-sref="page-subdirection">
<a ui-sref="page-subdirection({region: 'newValue'})">
It may also depend on what your final goal is. From what you're describing, it seems that you may actually be trying to pass in filters into a single state, not necessarily trying to route to 3 different states.
If in the case of filtering a single state, I'd recommend using query parameters as described in the docs (https://github.com/angular-ui/ui-router/wiki/URL-Routing). The advantage is that query parameters are not required or used from a routing point of view, they're simply parameters that your controller will utilize to filter data or tweak the view.
Snippet from above doc link:
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"

AngularJS: get url path without routeParams

Take this route for exmaple
when('/coordinator/editApplications/:appId', {
templateUrl: 'assets/app/templates/coordinator/editApplications.tpl.html',
module: "/coordinator",
})
Without using regular expressions, is there a property/method available in Angular that will allow me to get the path value with the parameters stripped off?
The value I would want for this example would be /coordinator/editApplications
This needs to be be universal because there could be multiple params or differently named params. I can't find anyway on the $route or $location to get this value as they all seem to contain the params.
Again, I know i can use regExp to do this but I want to avoid that. I also don't want to just add another property to the route w/ the non-param url value.

Dynamic nested routing in angularJs

Trying to implement the following route system with angular stateProvider:
some_domain.com/abc/pqr/xyz
Issue is the number of levels after "some_domain.com" can vary. It can be like
some_domain.com/abc/pqr/xyz
some_domain.com/abc/pqr
some_domain.com/abc
some_domain.com/abc/pqr/xyz/1234
tried the many approaches for this in the state definition:
$stateProvider
.state('dummy', {
url: '/*',
templateUrl: 'views/home.html',
controller: 'LandingPage'
});
}]);
Can anybody help me regarding this?
As the parameters you are talking about are part of the route itself, so you cannot have a single route to handle all these cases. I can think of 2 ways to go about it :
Create a seperate route for each case.
Use optional parameters if you can modify the logic accordingly. If the parameters can be optional, they should sent as query parameters instead of making them part of URL as such. So, the URL becomes something like :
url: '/dummy?param1?param2?param3'

Force ui-router to reload on querystring changes

I have a state defined something like this:
$stateProvider.state("app.search", {
url: "/search?q",
templateUrl: "App/Search.html",
controller: "SearchController as vm",
});
When I want to navigate to the search page and specify a search term, I can do something like this:
$location.path("search").search({ q: "stuff" });
Which effective resolves to a url along the lines of #/search?q=stuff. If I then change the search term to "things", the search page correctly reactivates and searches as expected.
However, I'd like to be able to specify "random" querystring parameters which have not been defined in my url, and then have those parameters also reload the state. (Note: this is why I'm using $location.path instead of $state.go to change the URL). So for example, if I was searching for food, my URL may be:
#/search?q=stuff&type=food
And then I may be filtering on spice levels, which may change the URL to:
#/search?q=stuff&type=food&spice=medium
(etc).
The problem though, is that since I haven't defined all the other query string parameters (type and spice in this example), ui-router doesn't reload my page.
I can't use $state.go with { reload: true } because it ignores the unspecified parameters.
Is there a way I can trigger a page reload using "unspecified" querystring parameters? This post suggests I can make the route /search* but that doesn't appear to work for me.
I haven't found a nice way to do this yet with ui-router. If you're willing to sacrifice your integrity a little bit you could utilize an alternate format for your query params. You can use a single http url query param object and list all your criteria using a custom format.
e.g.
/search/q?stuff:type=food:spice=cinnamon
The route setup would look like:
$stateProvider.state("app.search", {
url: "/search?q",
templateUrl: "App/Search.html",
controller: "SearchController as vm",
});
and to navigate with the extra parameters:
$state.go('app.dashboard', { q: 'stuff:type=dessert:spice=cinnamon'});
to access the parameters on the route:
$state.params.q
You can then just parse out the 'stuff' value and then each name/value pair that was passed in.
PS> I'm not sure whether the colon (:) separator is valid but it was used to illustrate the proposed idea.

how to define a regex parameter in angular-ui-router to pick only from a set of defined strings

I want my angular-ui-router in a specific state to be able to enter only if the parameter is one of the following strings:
"site1"
"site2"
"site3"
This is my state:
$stateProvider.state("error", {
url: '/error/{sub: (site1 | site2 | site3)}',
templateUrl: 'views/error.html',
controller: 'ErrorCtrl'
});
But for some reason this doesn't work.
How would i go and define this regex properlly?
I found the problem.
According to the documentation of angular-ui-router:
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.
So the solution is just to take them down.
I also had to remove all whitespaces since they actually account for letters.
$stateProvider.state("error", {
url: '/error/{sub:site1|site2|site3}',
templateUrl: 'views/error.html',
controller: 'ErrorCtrl'
});

Resources