Take the 2-minute tour ×
Stack Overflow is a question and answer site for professional and enthusiast programmers. It's 100% free, no registration required.

Forgive if this is a silly question, but I am new to AngularJS, and for the life of me I cannot figure out why this nested view setup is not operating as expected.

I have a top-level <div>:

<div class="grid" ui-view></div>

Which has a module populating its view:

angular.module('planttheidea',[
    'ui.router',
    'planttheidea.header',
    'planttheidea.mainContent',
    'planttheidea.footer'
])
    .config(['$stateProvider','$urlRouterProvider',function($stateProvider,$urlRouterProvider){
        $stateProvider
            .state('base',{
                url:'',
                views:{
                    '':{
                        templateUrl:'app/layout.view.html',
                        controller:'LayoutCtrl'
                    }
                }
            });

        $urlRouterProvider.otherwise('/');
    }])
    .controller('LayoutCtrl',function LayoutCtrl($scope,$stateParams){
        var layout = this;
    }); 

I then have the contents of that layout view:

<header ui-view="header" class="header menubar col-p-100" ng-controller="HeaderCtrl"></header>
<main ui-view="main-content" class="main-content grid col-p-100" ng-controller="MainContentCtrl"></main>
<footer ui-view="footer" class="footer col-p-100" ng-controller="FooterCtrl"></footer>

And right now I have the header and footer with their own modules:

angular.module('planttheidea.header',[
    'planttheidea.header.menu'
])
    .config(['$stateProvider','$urlRouterProvider',function($stateProvider){
        $stateProvider
            .state('base.header',{
                url:'',
                views:{
                    'header@base':{
                        templateUrl:'app/components/header/header.view.html',
                        controller:'HeaderCtrl as layoutHeaderCtrl'
                    }
                }
            });
    }])
    .controller('HeaderCtrl',function HeaderCtrl($scope,$stateParams){
        var layoutHeaderCtrl = this;
    });

angular.module('planttheidea.footer',[])
    .config(['$stateProvider','$urlRouterProvider',function($stateProvider){
        $stateProvider
            .state('base.footer',{
                url:'',
                views:{
                    'footer@base':{
                        templateUrl:'app/components/footer/footer.view.html',
                        controller:'FooterCtrl as layoutFooterCtrl'
                    }
                }
            });
    }])
    .controller('FooterCtrl',function FooterCtrl($scope,$stateParams){
        var layoutFooterCtrl = this;
    });

You can see they are nearly identical. The header module is working perfectly (it even has its own nested ui-view for the menu, which works just fine), however the footer code is not routing the ui-view. What's even wackier is that if I copy-and-paste the footer code into the header module (using just the base.header state), it works as desired:

angular.module('planttheidea.header',[
    'planttheidea.header.menu'
])
    .config(['$stateProvider','$urlRouterProvider',function($stateProvider){
        $stateProvider
            .state('base.header',{
                url:'',
                views:{
                    'header@base':{
                        templateUrl:'app/components/header/header.view.html',
                        controller:'HeaderCtrl as layoutHeaderCtrl'
                    },
                    'footer@base':{
                        templateUrl:'app/components/footer/footer.view.html',
                        controller:'FooterCtrl as layoutFooterCtrl'
                    }
                }
            });
    }])
    .controller('HeaderCtrl',function HeaderCtrl($scope,$stateParams){
        var layoutHeaderCtrl = this;
    })
    .controller('FooterCtrl',function FooterCtrl($scope,$stateParams){
        var layoutFooterCtrl = this;
    });

I have validated the code is being loaded correctly (I did a console.log() in the controller of the footer just to make sure it was running at all), so I am thinking it must be the segregation into the two modules. Do I need to declare all the layout views in the same state, or is there a way to break them up into separate modules as I have done here? I must believe its possible because I did so with the nested menu module, however that was nested whereas the footer is in parallel, so I'm worried that may be the issue.

Any assistance would be most appreciated, but do keep in mind I am in the process of learning AngularJS, so while working code would be awesome (obviously), an explanation of why would be even more awesome.

share|improve this question

1 Answer 1

up vote 1 down vote accepted

So there are a few problems in here that I would correct first and see if that fixes your issue.

Lets assume you have a DOM layout like this:

<div ui-view>
    <div ui-view="view1"></div>
</div>

Then your routes would look like this (it will be the same even if it is split into different modules as long as you inject the other modules into the master one - which you are)

.config(['$stateProvider','$urlRouterProvider',function($stateProvider){
        $stateProvider
            .state('base',{
                url:'/',
                templateUrl:'template.html',
                controller:'Ctrl as ctrl'
            })
            .state('base.view1',{
                url:'/view1',
                views:{
                    'view1':{
                        templateUrl:'template.html',
                        controller:'Ctrl2 as ctrl2'
                    }
                }
            });
    }])

Some things to notice:

Because my base state is going directly into a ui-view with no value i don't have a view object with an empty string, i just exclude it altogether.

Also my base.view1 has a url assigned to it, as it is a different state.

Because my ui-view="view1" is inside the ui-view of my base state I don't need to claim the view as view1@base below is a scenario where I would need to do that.

<div ui-view="main"></div>


.config(['$stateProvider','$urlRouterProvider',function($stateProvider){
        $stateProvider
            .state('base',{
                url:'/',
                views : {
                    'main' : {
                        templateUrl:'template.html',
                        controller:'Ctrl as ctrl'
                    }
                }

            })
            .state('base.view1',{
                url:'/view1',
                views : {
                    'main@base' : {
                        templateUrl:'template.html',
                        controller:'Ctrl as ctrl'
                    }
                }

            });
    }])

In this example I want base.view1 to "takeover", so to speak, the view that base was in.

I don't really understand why you have a header and footer as different states. Perhaps this is because I don't have the big picture of your project, but it seems out of the norm. Usually you would include your header and footer as part of the parent state and then have your main-content area switch out with different nested states.

Hope this made sense, let me know if I can clarify more.

share|improve this answer
    
The last paragraph gave me pause, so I stepped back and realized I was making the age-old mistake of over-abstracting. I was coming at the inclusion of templates very much like PHP's require_once, but a state is just that ... a state of the application, and the header / footer do not reflect a change in state. I've refactored based on this understanding and everything is working as expected. Accepted because no one else should spend time on my mistake, and upvoted because it pointed me in the right direction. Thanks! –  PlantTheIdea Jan 18 at 15:53
    
No problem, glad you got everything working! –  Jacob Carter Jan 18 at 18:42

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.