可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
Given an HTML file like so:
<html>
<header ui-view="header"></header>
<div class="main" ui-view></div>
<footer ui-view="footer"></footer>
</html>
How would one create a layout state that fills the "header" with a header template, the footer with a footer template, and then allow child states to fill the empty ui-view?
I suppose the empty ui-view could also be named something like ui-view="main".
回答1:
One way is to make a global 'root' state. So, each and every state will be it's child.
Like root.pages, root.pages.edit, root.settings, etc.
Then you can provide default templates for header and footer.
Another way, different approach, that I use personally, is to use ng-include
for header and footer.
<body ng-controller="AppCtrl">
<div id="header" ng-include="'header.tpl.html'"></div>
<div class="main_container" ui-view>
</div>
</body>
Btw. I use seperate controller in header.tpl.html, that is a child of main AppCtrl.:
<div id="header" ng-controller="HeaderCtrl"> ....
回答2:
try this, practically your header and footer are static templates but you can add the controllers in case you need to add some dynamic functionality to it, the header and the footer will be included by default since the route is '', so try that out:
app.config(['$stateProvider', function($stateProvider){
$stateProvider
.state('root',{
url: '',
abstract: true,
views: {
'header': {
templateUrl: 'header.html',
controller: 'HeaderCtrl'
},
'footer':{
templateUrl: 'footer.html',
controller: 'FooterCtrl'
}
}
})
.state('root.home', {
url: '/',
views: {
'container@': {
templateUrl: 'homePage.html'
}
}
})
.state('root.other', {
url: '/other',
views: {
'container@': {
templateUrl: 'other.html'
}
}
});
}]);
Edit:
to me the best place to set the views should be in the index.html
and something like this code:
<header>
<div ui-view="header"></div>
</header>
<div ui-view="container">
</div>
<footer id="mainFooter" ui-view="footer">
</footer>
回答3:
There is actually a very easy way to do that.
1. Create a layout state
$stateProvider
.state('master', {
abstract: true,
views: {
layout: {
templateUrl: '/layouts/master.html',
}
}
});
2. Use unnamed view state inside the layout
<!-- layouts/master.html -->
<div ui-view></div>
3. Create a view state
$stateProvider
.state('home', {
url: '/',
templateUrl: '/views/home.html',
parent: 'master',
});
4. Use named layout state as root state
<!-- home.html -->
<body ui-view="layout"></body>
回答4:
Based on the answer by Jack.the.ripper, I created this solution.
Casus: I have a slight variation that I actually want 2 layouts. One Public and one Private.
In Meteor with Blaze and Iron Router there was a nice solution that you could just define which master template will be used for a certain route.
This I have now been able to set up thanks to Jack!
NOTE: Code will be in Jade and Coffee and it's a Meteor + Angular project.
Use http://js2.coffee/ to convert to Javascript.
# the ROUTER part
#
angular.module( 'myApp' ).config(
( $urlRouterProvider, $stateProvider, $locationProvider ) ->
$locationProvider.html5Mode true
$stateProvider
.state( 'private',
url: ''
abstract: true
views:
'app':
templateUrl: 'client/views/layouts/privateLayout.html'
)
.state( 'public',
url: ''
abstract: true
views:
'app':
templateUrl: 'client/views/layouts/publicLayout.html'
)
.state( 'private.home',
url: '/'
views:
"container@private":
templateUrl: 'client/views/home/home.html'
)
.state( 'public.login',
url: '/login'
views:
"container@public":
templateUrl: 'client/views/session/login.html'
)
$urlRouterProvider.otherwise '/'
)
This is the index file where the app view is defined that will utilise the abstract state defined in the router.
head
meta(name="viewport" content="width=device-width, initial-scale=1")
base(href="/")
body(layout="column")
div(ui-view="app" layout="column" flex)
Then the Private Layout with its container view.
div(layout="column" flex)
div(ng-controller="AppCtrl" layout="row" flex)
//- some private Layout stuff happening here....
md-content(flex layout-padding)
div(ui-view="container" layout="column")
and finally the Public Layout with its container view
div.public-layout(layout="column" flex)
div(ui-view="container" layout="column" flex)
With this setup I can set the login page to use the abstract public layout by stating in the view of this route that it should uses views container@public, meaning from Public view, use the view Container. In this view load the login.html.
Same goes for the home page, this one loads the container@private meaning the Container view of the Private view.
This seems to work like a charm.
Thanks very much Jack, and also the author of the answer on Angular UI Router - Nested States with multiple layouts which helped me to get towards the final solution.
Cheers
回答5:
Similar to jack.the.ripper's way, you could also do it the way it works for me.
app.config(function($stateProvider, $urlRouterProvider) {
$stateProvider
.state('root', {
/* The Root State */
views: {
'': {
abstract: true,
templateUrl: 'master.html',
controller: 'mainController'
},
'header@root': {
templateUrl: 'header.html',
controller: 'headerController',
},
'footer@root': {
templateUrl: 'footer.html',
controller: 'footerController'
},
},
})
.state('root.index', {
/* The Index State */
url: '/',
views: {
'content': {
templateUrl: 'index.html',
controller: 'indexController'
}
},
.state('root.other', {
/* The Other State */
url: '/',
views: {
'content': {
templateUrl: 'other.html',
controller: 'otherController'
}
},
});
});
In our index.html
we will only have a <ui-view></ui-view>
The master.html
will look like
<div ui-view="header"></div>
<div ui-view="content"></div>
<div ui-view="footer"></div>
Why I chose this approach, is I don't have to create a separate global controller, and my mainController
will be the global controller.
回答6:
Instead of using routes at all for the header and footer, I would use Angular components now that they are available in 1.5x.
It is way more flexible and you don't have to deal with root.whatever or ngInclude. I go into it in more detail here: https://stackoverflow.com/a/41093339/2416007, but essentially you:
1. Create Component
(function () {
'use strict';
angular
.module('layout')
.component('layoutHeader', {
templateUrl: 'layout/header.html',
bindings: {},
controller: Controller
});
Controller.$inject = [];
function Controller() {
var ctrl = this;
initialize();
////////////////////
function initialize(){
}
}
}());
2. Add it to your index.html page or similar
<layout-header></layout-header>
3. Same for footer
<layout-footer></layout-footer>
4. The result in the body is
<layout-header></layout-header>
<main ui-view></main>
<layout-footer></layout-footer>