angularjs ui-router - how to build master state which is global across app
AngularjsAngular UiAngularjs Problem Overview
<html ng-app="app">
<head>
...
</head>
<body>
<div id="header"></div>
<div id="notification"></div>
<div id="container"></div>
<div id="footer"></div>
</body>
</html>
With the given structure of the app (derived from angular-app):
- header: Here the site navigation, login/out toolbar etc comes in. This is dynamic and has it's own Controller
- notification: Global notification container.
- container: This used to be my
<ng-view>
. So this is where all other modules loads in. - footer: Global footer.
How do the state hierarchy looks like? I've gone through the example which shows a single module (contacts) but typically an app would have a global (root) state and inside the root state other module states are rendered.
What I'm thinking is my app
module probably have the root
state and then each module should have it's own state and I have to inherit from root
state. Am I right?
Also from ui-state
example, they have used both $routeProvider
and $urlRouterProvider
as well as $stateProvider
has url defined. My understand was $stateProvide
also handles routing. If I'm wrong, which provider should I use for routing?
EDIT: http://plnkr.co/edit/wqKsKwFq1nxRQ3H667LU?p=preview
Thanks!
Angularjs Solutions
Solution 1 - Angularjs
The approach you took in your plunker is close. @ben-schwartz's solution demonstrates how you'd set defaults in your root state for the three essentially-static views. The thing missing in your plunker is that your child states still need to reference the top container view.
.state('root',{
url: '',
views: {
'header': {
templateUrl: 'header.html',
controller: 'HeaderCtrl'
},
'footer':{
templateUrl: 'footer.html'
}
}
})
.state('root.about', {
url: '/about',
views: {
'container@': {
templateUrl: 'about.html'
}
}
});
Note views: { 'container@': ...}
instead of just templateUrl: ...
in 'root.about'
What you may also be asking about is whether you can have modules define their own state-sets, which you then attach to your app's state hierarchy. A sort of plug-and-play for the routes/states each module provides.
To achieve this you'll have tightly couple your modules to your main app.
In the module:
angular.module('contact', ['ui.router'])
.constant('statesContact', [
{ name: 'root.contact',
options: {
url: 'contact',
views: {
'container@': {
templateUrl: 'contacts.html'
}
},
controller:'ContactController'
}}
])
.config(['$stateProvider', function($stateProvider){
}])
Then, in the app:
var app = angular.module('plunker', ['ui.router', 'contact']);
app.config( ['$stateProvider', 'statesContact',
function($stateProvider, statesContact){
$stateProvider
.state('root',{ ... })
.state('root.home', { ... })
.state('root.about', { ... })
angular.forEach(statesContact, function(state) {
$stateProvider.state(state.name, state.options);
})
}]);
This means all your modules will need to be compatible with this pattern set out in your app. But if you accept this constraint you can then choose include any combination of your modules, and their states magically get added to your app. If you want to get even fancier, you can modify state.options.url
in your statesModuleName
loop to, for example, prefix your module's url structure.
Also note that the module ui.compat
is only necessary when you are transitioning from $routeProvider
to $stateProvider
. You should normally use ui.state
instead.
Also don't forget to adjust in header.html $state.includes('root.contact')
)
Solution 2 - Angularjs
Although confusing, the FAQ in the ui-router wiki seems to say that this isn't possible: ui-router/wiki/faq
One approach is to allow each feature to define it's own root state (as in this example: AngularJS State Management with ui-router)
Another would be to define the entire state hierarchy in your "myApp" module and just leverage controllers etc. from the dependent modules. This works especially well when you are maintaining a mobile and desktop site; define each site's state hierarchy in a mobileApp and desktopApp module, and have the functionality (e.g. controllers, services, etc.) shared in separate modules.
Solution 3 - Angularjs
It depends how you prefer to approach it.
All scopes inherit from rootScope so you may place global state there. The NG literature specifically mentions that you should only put global state there, and not functionality. Functionality required across the system belongs in services, and specifically you should not implement services whose sole purpose is to retain state. All the advice seems to be implicitly shared in the context of how it either facilitates or hampers your ability to easily do end to end testing.