-
Notifications
You must be signed in to change notification settings - Fork 3k
Frequently Asked Questions
How Tos:
- How to: Configure ui-router from multiple modules
- How to: Open a dialog/modal at a certain state
- How to: Animate ui-view with ng-animate
- How to: Configure your server to work with html5Mode
- How to: Set up a default/index child state
- How to: Use require.js with ui-router
- How to: Lazy load states
- How to: Make trailing slash optional
- How to: Create rules to prevent access to a state
- How to: Prevent to prevent nested ui-sref objects from all firing/triggering
Popular Issues:
With #492 merged (v0.3.0), you can now register states in any order and across modules. You can register children before the parent state exists. It will queue them up and once the parent state is registered then the child will be registered. Note: You still need to manage module dependencies.
angular.module('main', ['main.page1']).config(function($stateProvider) {
$stateProvider.state('main', {...})
});
angular.module('main.page1', []).config(function($stateProvider) {
$stateProvider.state('main.page1', {...})
});
Note: Remember only to declare dependencies once per module creation, so if you make several calls to angular.module
for the same module, only the first should define dependencies.
How do I tell ui-router that I'd like a certain state to open a dialog or modal instead of swapping out ui-view templates?
Here's an example of to do it using ui-bootstrap's $modal service. This example only specifies an onEnter function. There is no template, controller, etc. So essentially the modal is shown, then when its closed it returns to the items
state. You are still responsible for handling what state your app transitions to when the modal closes.
$stateProvider.state("items.add", {
url: "/add",
onEnter: function($stateParams, $state, $modal, $resource) {
$modal.open({
templateUrl: "items/add",
resolve: {
item: function() { new Item(123).get(); }
},
controller: ['$scope', 'item', function($scope, item) {
$scope.dismiss = function() {
$scope.$dismiss();
};
$scope.save = function() {
item.update().then(function() {
$scope.$close(true);
});
};
}]
}).result.then(function(result) {
if (result) {
return $state.transitionTo("items");
}
});
}
});
How do I add animation effects to the ui-view templates that are loaded?
Make sure you are using the latest unstable version of Angular (1.1.5 as of writing this, 1.1.4 had a totally different syntax that won't work with this example).
Add ng-animate="'view'"
to your ui-view:
<div ui-view ng-animate="'view'"></div>
The animation class doesn't have to be "view" that's just an example. Then create a view-enter, view-enter-active, view-leave, and view-leave-active class styles in your css. The "-enter" class will be used to set up the animations and the "-enter-active" will be used as the end point of the animation.
When I add $locationProvider.html5Mode(true)
, my site will not allow pasting of urls. How do I configure my server to work when html5Mode is true?
Warning: If you have server side rewrites already set up and still experience issues, please see Issue: assets and templates are not loading.
When you have html5Mode enabled, the #
character will no longer be used in your urls. The #
symbol is useful because it requires no server side configuration. Without #
, the url looks much nicer, but it also requires server side rewrites. Here are some examples:
Apache Rewrites
<VirtualHost *:80>
ServerName my-app
DocumentRoot /path/to/app
<Directory /path/to/app>
RewriteEngine on
# Don't rewrite files or directories
RewriteCond %{REQUEST_FILENAME} -f [OR]
RewriteCond %{REQUEST_FILENAME} -d
RewriteRule ^ - [L]
# Rewrite everything else to index.html to allow html5 state links
RewriteRule ^ index.html [L]
</Directory>
</VirtualHost>
Express Rewrites
var express = require('express');
var app = express();
app.use('/js', express.static(__dirname + '/js'));
app.use('/dist', express.static(__dirname + '/../dist'));
app.use('/css', express.static(__dirname + '/css'));
app.use('/partials', express.static(__dirname + '/partials'));
app.all('/*', function(req, res, next) {
// Just send the index.html for other files to support HTML5Mode
res.sendfile('index.html', { root: __dirname });
});
app.listen(3006); //the port you want to use
Also, read this great tutorial about Angular + Express
When I have several nested states, how do I set up one particular child state to be the index, or default, state of its parent?
Let's say you have the following states:
- 'parent'
- 'parent.index'
- 'parent.page2'
You want that when the user tries to go to 'parent', it will automatically go to 'parent.index'.
- First, add a property to the
'parent'
state ofabstract:true
. You want to set this since this state will never be activated directly, users will always go to one of it's child states instead.
// url '/home', but you'll never see this state directly
.state('parent', {url: '/home', abstract: true} )
- Then do one of the following:
- Give the
'parent.index'
state an empty url. This will make it match the same url as it's parent state url, because it appends nothing to the parent url.
$stateProvider
.state('parent', {url: '/home', abstract: true} )
// ALSO url '/home', overriding its parent's activation
.state('parent.index', {url: ''} )
- Or if you want the
'parent.index'
url to be non-empty, then set up a redirect in your module.config using $urlRouterProvider:
$urlRouterProvider.when('/home', '/home/index');
$stateProvider
.state('parent', {url: '/home', abstract: true} )
// url '/home/index'
.state('parent.index', {url: '/index'} )
How can I load ui-router with Require.js? Note: This is not lazy loading, for that see the section below.
The answer to this FAQ can be found here: https://github.com/angular-ui/ui-router/issues/479#issuecomment-26232775
How can I load/define a state right when a user attempts to navigate to it?
Ed, please fill in info on:
- storing providers for later use
- showing how to use the $stateNotFound event
- how to set the retry promise on the event
- how to define the unfoundState using stored provider and resolve the promise
How can I have my routes be activated when the url contains either a trailing slash or not?
Add this snippet to your module's config function. It creates a rule on $urlRouterProvider that maps all urls that are missing a trailing slash to the same url but with the trailing slash appended.
You'll need to specify all urls with a trailing slash if you use this method.
$urlRouterProvider.rule(function($injector, $location) {
var path = $location.path()
// Note: misnomer. This returns a query object, not a search string
, search = $location.search()
, params
;
// check to see if the path already ends in '/'
if (path[path.length - 1] === '/') {
return;
}
// If there was no search string / query params, return with a `/`
if (Object.keys(search).length === 0) {
return path + '/';
}
// Otherwise build the search string and return a `/?` prefix
params = [];
angular.forEach(search, function(v, k){
params.push(k + '=' + v);
});
return path + '/?' + params.join('&');
});
Note: All routes in app/scripts/app.js
must be redefined with trailing /
. This means that routes such as /things/:id
become /things/:id/
as well.
How can I have my states guarded by some logic that determines if a user is allowed to access that state? What if I also want to redirect them based on that logic?
Example: Uses the data
object on the state config to define a rule function that will run logic against the user (here using an example service called $currentUser). The $stateChangeStart
handler catches all state transition and performs this rule check before allowing the transition, potentially blocking it and/or redirecting to a different state.
app.config(function($stateProvider) {
$stateProvider.state('privatePage', {
data: {
rule: function(user) {
// ...
}
});
});
app.run(function($rootScope, $state, $currentUser) {
$rootScope.$on('$stateChangeStart', function(e, to) {
if (!angular.isFunction(to.data.rule)) return;
var result = to.data.rule($currentUser);
if (result && result.to) {
e.preventDefault();
$state.transitionTo(to, result.params);
}
});
});
In some cases you'll want to nest elements that both (or more) contain a ui-sref attribute. If you click the deepest nested element it will trigger all the ui-srefs by default. To stop propogation do the following:
<div ui-sref="root.child" ng-click="$event.stopPropagation()">
Child-detail
</div>
This may be related to a nasty issue where assets and templates are not loading. It showed up during Angular 1.1.5 and is a bug in the core library. This tree helps you decide what response to take.
1) Using Angular 1.1.5?
---Yes:
See solution below.
---No:
2) Using HTML5 urls?
--------------No:
No base tag needed.
--------------Yes:
3) Deploying to root?
--------------------------Yes:
No base tag needed.
--------------------------No:
See solution below.
You'll need to either:
- Use absolute anchor urls (preferred)
- Add tag to your index.html BUT all anchor urls will be broken for you :/
If you've chosen #2, then add the following tag to your index.html head:
<head>
...
<base href="/"></base>
...
</head>
... if your application is running at the root of your domain.
... and if your application is running in a subdirectory, specify that subdirectory (e.g. 'myapp'):
<head>
...
<base href="/myapp/"></base>
...
</head>
Bugs that were solved by this solution: 89, 200, 208, 250, SO 17200231
Other Resources:
- See bug in core: issues/2799
- Pull that fixes it: pull/2969
Don't do that.