0

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?

Ferie
  • 1,358
  • 21
  • 36

1 Answers1

1

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

Chris T
  • 8,186
  • 2
  • 29
  • 39
  • Thanks for the answer, btw I am not getting what I want: if I define an empty view named ```content``` in the ```mainCommonState```, and then define it inside the child states (using content@mainCommonState), it is not working... Can you please see the plunker that I have created? http://plnkr.co/edit/oTQzGVpvsjKiM22NGo5v?p=preview – Ferie Oct 18 '17 at 14:07
  • 1
    http://plnkr.co/edit/GCKGErz68iuEir6Vo7sl?p=preview . you need to target the `ui-view` tag. the `
    ` tag was created in the *root* template, not in any state. Target it using `content@` or `!content` as shown in the examples
    – Chris T Oct 22 '17 at 18:32
  • Thanks again for the answer, in the end I preferred to use the absolute path (```!content```) because it makes more sense to me. Now the second level nested view is not automatically set as default even if the url is defined in the first tab instead of the first page (like suggested here https://stackoverflow.com/questions/17001589/directing-the-user-to-a-child-state-when-they-are-transitioning-to-its-parent-st?rq=1). The updated plunker is here http://plnkr.co/edit/oTQzGVpvsjKiM22NGo5v?p=preview , have you got an idea on how to solve this? – Ferie Oct 24 '17 at 18:41