58
<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!

Amitava
  • 5,013
  • 9
  • 37
  • 50

3 Answers3

53

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'))

updated plunker

Andy Zarzycki
  • 2,601
  • 19
  • 10
  • So basically this approach will tight couple my modules with app module which I don't think is a good approach. I could define all the states in my app module but I just don't like the idea to have all the controller specific resolves in there as well. I wish there were a way to define resolves separately in respective modules. – Amitava May 16 '13 at 04:21
  • @SamSerious did you find any solution that isolate the route from app? – Deividi Cavarzan Aug 01 '13 at 22:27
  • @DeividiCavarzan I was not exactly looking for isolating routes, but to put route definitions to respective modules for which I have solution. I'll update if you are looking for the same. – Amitava Aug 02 '13 at 02:04
  • @SamSerious yes is that I'm looking for. I really appreciate if you can post this solution! – Deividi Cavarzan Aug 02 '13 at 13:53
  • 1
    In the updated plunker example, clicking the header link to the contacts view does load contacts.html, but the title property from the ContactController isn't available, and does show in the view? – chrismarx Aug 02 '13 at 15:17
  • Nice that seems to work, I'm using a similar strategy with abstract views and absolute state/view targeting – chrismarx Aug 05 '13 at 15:48
  • Plunkers work for me. The problem with the "updated plunker" is that the controller (in contact.js) should be inside the container@ dictionary. – eXt Oct 22 '13 at 10:05
5

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.

ben schwartz
  • 2,559
  • 1
  • 21
  • 20
  • OK here is what I'm trying. I'm still not sure how to bind root state with other module states. http://plnkr.co/edit/wqKsKwFq1nxRQ3H667LU?p=preview – Amitava May 13 '13 at 01:46
  • i forked your example and added a couple of things: http://plnkr.co/edit/oTjf0PIpxZE272TTLMhZ - in plunker you need to "transitionto" to get things started - either use a plain "ui-view" for your default templateUrl, or target a specific view from a specific state (in this case the "container" view from the root (empty string)state) – ben schwartz May 13 '13 at 02:52
  • I'm getting error `No such state 'contacts'` when I click contact tab. The whole idea of having each state definitions in their respective modules is that the module will be self contained also I can define resolve in the module itself. – Amitava May 13 '13 at 05:56
  • Sorry, I was just implementing what you had started. I've added in support for switching states. I also made the root (managing the common header and footer) abstract and included a concrete "home" state. http://plnkr.co/edit/oTjf0PIpxZE272TTLMhZ?p=preview – ben schwartz May 13 '13 at 18:31
  • 1
    @benschwartz I tried using that last plunker with some API data. Senario: I would be on `/about` and fetch some API data, toggle to `/home` then back to `/about` and the data would be gone. Do you have to pair data with state explicitly using `$stateProvider` or is there a way for it to know that new data is on the page and save the current state of how it was rendered? – Dan Kanze Jun 01 '13 at 22:07
  • @DanKanze I think that if you inject $statePararms where you need them then you'll have access to whatever makes up the hierarchy of externally interesting variables. If it's not in $stateParams, then ask yourself why you need it. From there it's up to you how you want to use them in controllers (and thereby in directives or html). – laurelnaiad Jun 20 '13 at 18:39
0

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.

grettke
  • 1,407
  • 7
  • 10
  • Just to be clear, you are talking about Angular-UI-Router, right? The way you talk about "global state", it seems you are talking generally, not about states specifically in that library. – trysis Mar 26 '15 at 18:11