Ferie
Ferie

Reputation: 1436

Nested states in Ui-Router with AngularJS

I would like to have a common state with all the common views like the header and the sidebar, and a template where I would like to load different views that can change when the state is changing.

I have an index HTML file like this:

...
    <div ui-view="header"></div>

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

    <div class="container">
        <div class="wrapper">
            <div ui-view="content"></div>
        </div>
    </div>
...

While the AngularJS config is something like this:

$stateProvider
    .state('mainCommonState', {
        url: '',
        abstract: true,
        views: {
            header: {
                templateUrl: 'app/common/header.html',
                controller: 'headerCtrl',
                controllerAs: 'vm'
            },
            sidebar: {
                templateUrl: 'app/common/sidebar.html',
                controller: 'sidebarCtrl',
                controllerAs: 'vm'
            },
            content: {}
        },
        resolve: {
            apiEnvironment: function ($q, environmentApiService) {
                var deferred = $q.defer();
                deferred.resolve(environmentApiService.getApiEnvironment());
                return deferred.promise;
            }
        }
    })
    .state('first-page-content', {
        url: '/first-page-content',
        parent: 'mainCommonState',
        views: {
            content: {
                templateUrl: 'app/components/first-page-content.html'
                controller: 'firstPageCtrl',
                controllerAs: 'vm'
            }
        }
    })
    .state('second-page-content', {
        url: '/second-page-content',
        parent: 'mainCommonState',
        views: {
            content: {
                templateUrl: 'app/components/second-page-content.html'
                controller: 'secondPageCtrl',
                controllerAs: 'vm'
            }
        }
    })
    .state('third-page-content', {
        url: '/third-page-content',
        parent: 'mainCommonState',
        views: {
            content: {
                templateUrl: 'app/components/third-page-content.html'
                controller: 'thirdPageCtrl',
                controllerAs: 'vm'
            }
        }
    })

For some reason this is not working: I have an empty view instead of the 3 templates that I would like to show in the content ui-view.

If I define a template (even a blank template) inside the the abstract state, the view that is always showing is the one inside the abstract state mainCommonState.

Where am I wrong?


1st Edit: UPDATE Following the first answer

Following the suggestion from Chris T, I have updated my code, but there still something missing.

I have created a Plunker so you can help me fixing the issues.


2nd Edit

Following the suggestions from Chris T, I have updated the code using the absolute path for the content view and now the contents are switching correctly.

I have updated the Plunker accordingly to that and introduced a new level of nesting view (tabs in the first page), and I would like to have the first tab active when the first page content is loaded.

If I follow these solutions and set empty the url of the first page and set it to the first tab instead, this is not working.

Any suggestions?

Upvotes: 0

Views: 333

Answers (1)

Chris T
Chris T

Reputation: 8216

Your views are targeting the wrong named ui-view.

.state('second-page-content', {
     url: '/second-page-content',
     parent: 'mainCommonState',
     views: {
         content: {
             templateUrl: 'app/components/second-page-content.html'
             controller: 'secondPageCtrl',
             controllerAs: 'vm'
         }
     }
 })

In this snippet, it targets the ui-view named content from the parent state which is mainCommonState. However, the content ui-view was not created in the mainCommonState. It was created in the root template.

Change your view declarations to target the view at the correct state, for example this targets the content view at the root state (which is named empty string):

.state('second-page-content', {
     url: '/second-page-content',
     parent: 'mainCommonState',
     views: {
         'content@': {
             templateUrl: 'app/components/second-page-content.html'
             controller: 'secondPageCtrl',
             controllerAs: 'vm'
         }
     }
 })

In ui-router 1.0 and higher you can also use absolute ui-view names by prefixing with an exclamation

.state('second-page-content', {
     url: '/second-page-content',
     parent: 'mainCommonState',
     views: {
         '!content': {
             templateUrl: 'app/components/second-page-content.html'
             controller: 'secondPageCtrl',
             controllerAs: 'vm'
         }
     }
 })

Read more about view targeting in the UI-Router views guide:

https://ui-router.github.io/guide/views#view-name-only

Upvotes: 1

Related Questions