I'm trying to create a basic layout system using nested states in angular using ui-router.
What I'd like is that any 'sub state' can override the 'layout'
partial of the top level state so that different site layouts can be used depending on the area of the site.
Given the following system/server/views/index.html:
<section data-ui-view="layout" ></section>
I'm trying to create a basic layout system using nested states in angular using ui-router.
What I'd like is that any 'sub state' can override the 'layout'
partial of the top level state so that different site layouts can be used depending on the area of the site.
Given the following system/server/views/index.html:
<section data-ui-view="layout" ></section>
and two layouts to be interchanged into the above: system/public/views/layouts/standard.html:
<div class="navbar navbar-inverse navbar-fixed-top" data-ui-view="header" data-role="navigation"></div>
<section class="container-fluid">
<section data-ui-view ></section>
</section>
system/public/views/layouts/full-width.html:
<div class="navbar navbar-inverse navbar-fixed-top" data-ui-view="header" data-role="navigation"></div>
<section class="container">
<section data-ui-view ></section>
</section>
sample content system/public/views/index.html
<div>some sample content</div>
and finally the router:
function($stateProvider, $urlRouterProvider) {
// For unmatched routes:
$urlRouterProvider.otherwise('/');
// states for my app
$stateProvider
.state('root', {
url: '/',
abstract: true,
views: {
'layout@': {
templateUrl: 'system/views/layouts/standard.html'
},
'header@root': {
templateUrl: 'system/views/partials/header.html'
}
}
})
.state('root.home', {
url: '',
views: {
'header@root': {
templateUrl: 'system/views/partials/header2.html'
},
'': {
templateUrl: 'system/views/index.html'
}
}
});
}
The above works. I'm able to swap the header in and out in the sub states, but if I add the override for the 'layout':
function($stateProvider, $urlRouterProvider) {
// For unmatched routes:
$urlRouterProvider.otherwise('/');
// states for my app
$stateProvider
.state('root', {
url: '/',
abstract: true,
views: {
'layout@': {
templateUrl: 'system/views/layouts/standard.html'
},
'header@root': {
templateUrl: 'system/views/partials/header.html'
}
}
})
.state('root.home', {
url: '',
views: {
'header@root': {
templateUrl: 'system/views/partials/header2.html'
},
'': {
templateUrl: 'system/views/index.html'
},
--->>> 'layout@': {
templateUrl: 'system/views/layouts/full-width.html'
}
}
});
}
When I add the 'layout@'
the date-ui-view="layout"
does get loaded correctly but the sub ui-views for the header and the unamed do not get replaced.
Any ideas what is going on here?
If this isn't something that should work what are alternative approaches?
Share Improve this question edited Oct 15, 2015 at 19:09 Radim Köhler 124k48 gold badges242 silver badges340 bronze badges asked Nov 13, 2014 at 6:08 Trevor JonesTrevor Jones 911 silver badge2 bronze badges 01 Answer
Reset to default 16The point is that overriding template in a child state 'root.home'
like this:
.state('root.home', {
...
->> 'layout@': {
templateUrl: 'system/views/layouts/full-width.html'
}
Is in fact totally removing any part from the parent 'root'. So now we have to use this for sibling views:
'[email protected]': { // see the 'root.home' after @
templateUrl: 'system/views/partials/header2.html'
},
'@root.home': { // see the 'root.home' after @
templateUrl: 'system/views/index.html'
},
See, that we used root.home
after @
. That is: We are explicitly saying:
- find the
ui-view="header"
and unnamedui-view=""
inside of this, current'root.home'
state.
There is no target existing in parent state 'home', because we totally skipped it.
The sample in documentation is in this case really self descriptive:
View Names - Relative vs. Absolute Names
(cited self describing snippet)
$stateProvider
.state('contacts', {
// This will get automatically plugged into the unnamed ui-view
// of the parent state template. Since this is a top level state,
// its parent state template is index.html.
templateUrl: 'contacts.html'
})
.state('contacts.detail', {
views: {
////////////////////////////////////
// Relative Targeting //
// Targets parent state ui-view's //
////////////////////////////////////
// Relatively targets the 'detail' view in this state's parent state, 'contacts'.
// <div ui-view='detail'/> within contacts.html
"detail" : { },
// Relatively targets the unnamed view in this state's parent state, 'contacts'.
// <div ui-view/> within contacts.html
"" : { },
///////////////////////////////////////////////////////
// Absolute Targeting using '@' //
// Targets any view within this state or an ancestor //
///////////////////////////////////////////////////////
// Absolutely targets the 'info' view in this state, 'contacts.detail'.
// <div ui-view='info'/> within contacts.detail.html
"[email protected]" : { }
// Absolutely targets the 'detail' view in the 'contacts' state.
// <div ui-view='detail'/> within contacts.html
"detail@contacts" : { }
// Absolutely targets the unnamed view in parent 'contacts' state.
// <div ui-view/> within contacts.html
"@contacts" : { }
// absolutely targets the 'status' view in root unnamed state.
// <div ui-view='status'/> within index.html
"status@" : { }
// absolutely targets the unnamed view in root unnamed state.
// <div ui-view/> within index.html
"@" : { }
});
Also, please do notice this important fact:
Scope Inheritance by View Hierarchy Only
Keep in mind that scope properties only inherit down the state chain if the views of your states are nested. Inheritance of scope properties has nothing to do with the nesting of your states and everything to do with the nesting of your views (templates).
It is entirely possible that you have nested states whose templates populate ui-views at various non-nested locations within your site. In this scenario you cannot expect to access the scope variables of parent state views within the views of children states.
I.e. - we cannot inherit from parent 'root' state anyhting, because the inheritance goes only view views...