I'm trying to define states in a 3 columns layout with the left column having the same content. But at this time, I had to repeat the templateUrl for all direct nested states.
.state('workspace', {
url: '/',
templateUrl: 'app/workspace/workspace.view.html'
})
.state('workspace.images', {
url: 'images',
views: {
'sidebar': {
templateUrl: 'app/workspace/sidebar.view.html',
controller : 'Workspace.SidebarController as vm'
},
'content': {
templateUrl: 'app/workspace/imageslist.view.html',
controller : 'Workspace.ImagesListController as vm'
}
}
})
.state('workspace.images.edit', {
url: '/:key',
templateUrl: 'app/workspace/editor.view.html',
controller : 'Workspace.EditorController as vm'
})
.state('workspace.documents', {
url: 'documents',
views: {
'sidebar': {
templateUrl: 'app/workspace/sidebar.view.html',
controller : 'Workspace.SidebarController as vm'
},
'content': {
templateUrl: 'app/workspace/documentslist.view.html',
controller : 'Workspace.DocumentsListController as vm'
}
}
});
As you can see, I have to repeat the "sidebar" template each time. I would like to be able to configure it form the abstract workspace state.
workspace.view.html contains the "sidebar" and "content" views :
<div class="sidebar" ui-view="sidebar">
<!-- view = sidebar -->
</div>
<div class="content" style="height: 100%" ui-view="content">
<!-- view = content-->
</div>
I think I should have a views object into the "workspace" state to define the "sidebar" templates and controllers and leave the "content" empty.
And have tried with and without named views but without success. When I do that, the sidebar template is never displayed but seems to be loaded (a wrong name cause a 404 in my console).
This will inject sidebar into parent... and child can use it or even change it:
.state('workspace', {
url: '/',
views: {
'': { templateUrl: 'app/workspace/workspace.view.html' }
'sidebar#workspace': {
templateUrl: 'app/workspace/sidebar.view.html',
controller : 'Workspace.SidebarController as vm'
},
}
})
.state('workspace.images', {
url: 'images',
views: {
'content': {
templateUrl: 'app/workspace/imageslist.view.html',
controller : 'Workspace.ImagesListController as vm'
}
}
})
...
We used views : {} even on parent state. The default unnamed is the original workspace.view.html
We also added named view 'sidebar#workspace' - the absolute name here means, that the name is 'sidebar' and its target is searched inside of the 'workspace' state templates
Related
I'm trying to build from scratch a very simple angular app using ui-router.
Every page will have the same appearance, and will have the following 4 sections (vertically, from top to bottom):
HEADER (common to all pages)
HORIZONTAL MENU (common to all pages)
CONTENT (this is the only content that will change)
FOOTER (common to all pages)
My index.html has <div ui-view></div> inside the <body> tag.
I also have a simple html file (portal.html) that contains the structure of every page:
<div ui-view="header"></div>
<div ui-view="menu"></div>
<div ui-view="content"></div>
<div ui-view="footer"></div>
I've have created a parent state, where I set the common sections to every page:
$stateProvider
.state('portal', {
url: '/portal',
templateUrl: 'app/main/portal.html',
views: {
header: {
templateUrl: 'app/main/section/header.html'
},
menu: {
templateUrl: 'app/main/section/menu.html'
},
footer: {
templateUrl: 'app/main/section/footer.html'
}
}
});
And some child states (one for each menu option - page), where I set the variable content to every page:
$stateProvider
.state('portal.home', {
url: '/home',
views: {
'content#portal': {
controller: 'HomeCtrl as homeVM',
templateUrl: 'app/portal/home.html'
}
},
resolve: { /* whatever */ }
})
// ... and so on ...
.state('portal.contactUs', {
url: '/contact-us',
views: {
'content#portal': {
controller: 'ContactUsCtrl as contactUsVM',
templateUrl: 'app/portal/contactUs.html'
}
},
resolve: { /* whatever */ }
});
But this won't display anything on screen... Am I missing something here?
I finally found a solution. What actually helped me was this StackOverflow post, that is quite similar to mine, and specially the plunker example shown there.
The mistake was in the state configuration object passed to the $stateProvider.state(). The parent state should be set the following way:
$stateProvider
.state('portal', {
url: '/portal',
views: {
'#': {
templateUrl: 'app/main/portal.html'
},
'header#portal': {
templateUrl: 'app/main/public/header.html'
},
'menu#portal': {
templateUrl: 'app/main/public/menu.html'
},
'footer#portal': {
templateUrl: 'app/main/public/footer.html'
}
}
});
I have a template with multiple named, nested views:
template 1:
<body>
<div ui-view></div>
</body>
template 2:
<header></header>
<div ui-view="left"></div>
<div ui-view="canvas"></div>
<div ui-view="right"></div>
and I have the following config setup:
.state("metricDashboard", {
url: "/metric-dashboard",
css: { href: "core/metric-dashboard/style.css" },
views: {
"": {
templateUrl: "core/metric-dashboard/view.html",
controller: 'MetricDashboard',
},
"left#metricDashboard": {
templateUrl: "core/metric-dashboard/partials/left.html",
controller: "MetricDashboardLeft",
},
"canvas#metricDashboard": {
templateUrl: "core/metric-dashboard/partials/canvas.html",
controller: "MetricDashboardCanvas"
},
"right#metricDashboard": {
templateUrl: "core/metric-dashboard/partials/right.html",
controller: "MetricDashboardRight"
}
}
})
How would I go about changing an individual route? For example, If I wanted to change "left#metricDashboard", but leave the "canvas" and "right" routes alone. I cannot seem to find a syntax without creating a new state and explicitly declaring all the routes over again.
Ok I'm guessing you want to create another state that will change only one of the views not all of them.
Let's call it left and make it a child of metricsDashboard
.state("metricDashboard", {
url: "/metric-dashboard",
css: { href: "core/metric-dashboard/style.css" },
views: {
"": {
templateUrl: "core/metric-dashboard/view.html",
controller: 'MetricDashboard',
},
"left#metricDashboard": {
templateUrl: "core/metric-dashboard/partials/left.html",
controller: "MetricDashboardLeft",
},
"canvas#metricDashboard": {
templateUrl: "core/metric-dashboard/partials/canvas.html",
controller: "MetricDashboardCanvas"
},
"right#metricDashboard": {
templateUrl: "core/metric-dashboard/partials/right.html",
controller: "MetricDashboardRight"
}
}
})
.state("metricDashboard.left", {
url: "left",
views: {
"left#metricDashboard" : {
templateUrl: "some.html",
controller: "AwesomeCtl",
controllerAs: "awe"
}
}
})
Now when you enter that state only the left view will change the others will remain as defined in the parent state metricDashboard.
I am struggling to create a container for next states, defined the states as views, divided into header, CONTAINER, footer.
The next state as an example would be the blogs, but I do not see a way of getting it into the view.
One idea was to start the HOME view as standard, but also failed.
view:
<main>
<header ui-view="header"></header>
<content ui-view="home"></content>
<footer ui-view="footer"></footer>
</main>
states:
.state('home',{
url:'/',
data: {
pageTitle: 'Home'
},
views: {
'': {
templateUrl: 'content/index.html',
},
'header#home': {
templateUrl: 'content/templates/header.html',
controller: 'HeaderController',
cache: false
},
'home#home': {
templateUrl: 'content/templates/home.html',
controller: 'IndexController',
cache: false
},
'footer#home': {
templateUrl: 'content/templates/footer.html',
//controller: 'FooterController',
cache: false
}
}
})
.state('home.blog',{
url : '/blog',
templateUrl : 'content/templates/blog.html',
controller : 'BlogController',
data: { pageTitle: 'Blog' },
access: {requiredLogin: false}
})
SUCCESS! :)
Plunker Example: http://plnkr.co/edit/yRgqiAeEVQl2WVajGgG0?p=preview
In the updated question above, you've used this plunker to show how you made it working:
.state('home',{
url:'',
abstract: true,
views: {
'': {
templateUrl: 'index.html' // this
},
'header#home': {
templateUrl: 'header.html'
},
'footer#home': {
templateUrl: 'footer.html'
}
}
})
.state('home.index',{
url : '/',
templateUrl : 'home.html'
})
.state('home.blog',{
url : '/blog',
templateUrl : 'blog.html',
});
While that solution is working, it is in fact not a way you/we should go. Because the parent state 'home', injects into unnamed view itslef - templateUrl: 'index.html'
So, now there are again views header and footer, but they do differ from the root (original index.htm). Their absolute name would be 'header#home' and 'footer#home' (as used int the code snippet) - and all seems to be working.
But that is redundant. Unless we will move the layout into some 'layout' state and 'layout.html'
Angular UI Router - Nested States with multiple layouts
Nested states or views for layout with leftbar in ui-router?
Why redundant? Because index.html already is in play (as a root) and it contains these targets. their absolute name is 'header#' and 'footer#'. And that should be the way to go.
To make it clear, there is an updated plunker and its snippets:
.state('home',{
url:'',
abstract: true,
views: {
'': {
template: '<div ui-view=""></div>'
},
'header': {
templateUrl: 'header.html'
},
'footer': {
templateUrl: 'footer.html'
}
}
})
.state('home.index',{
url : '/',
templateUrl : 'home.html'
})
.state('home.blog',{
url : '/blog',
templateUrl : 'blog.html',
});
Check the update here
I am creating a web app to help students in science, history and math. When you first land on the site I have a home/landing page. When you click get started I route to /exam/instructions. Each of my steps instructions, math and science our templates that I load into the ui-view="exam-detail". Currently the whole ui-view loads when I navigate to and from instructions through sciences. Ideally I simply want an area for pagination and an area for the subject matter and only want the ui-view="exam-detail" to update with the correct template.
I have not used UI-Router at all and any assistance would be greatly appreciated.
index.html
<div ui-view></div>
state-exam>exam.html
<div class="state-exam">
<nav ui-view="exam-pagination"></nav>
<section ui-view="exam-detail"></section>
</div>
route.js
(function() {
'use strict';
angular
.module('studentPortal')
.config(routeConfig);
function routeConfig($stateProvider, $urlRouterProvider) {
$stateProvider
.state('home', {
url: '/',
templateUrl: 'app/main/main.html',
controller: 'MainController',
controllerAs: 'main'
})
.state('exam', {
url: '/exam/:step',
abstract: true,
templateUrl: 'app/state-exam/exam.html',
controller: 'ExamController',
controllerAs: 'examController',
})
.state('exam.instructions', {
url: '/instructions',
views: {
'exam-pagination':{
templateUrl: 'app/state-exam/exam-pagination.html'
},
'exam-detail' : {
templateUrl: 'app/state-exam/exam-instructions.html'
}
}
})
.state('exam.math', {
url: '/math',
views: {
'exam-pagination':{
templateUrl: 'app/state-exam/exam-pagination.html'
},
'exam-detail' : {
templateUrl: 'app/state-exam/exam-math.html'
}
}
});
$urlRouterProvider.otherwise('/');
}
})();
There is a working plunker
There is a similar Q & A in fact, with working plunker:
Angular UI Router - Nested States with multiple layouts
Solution here, is to move the static view from child to parent. It won't be reloaded for each child (view is reloaded only if parent state is changed). We will use absolute naming (see included links for more details)
So this is the code adjustment
.state('exam', {
url: '/exam/:step',
abstract: true,
// the root view and the static pagination view
// will be defined here, so we need views : {}
views: {
'':{
templateUrl: 'app/state-exam/exam.html',
controller: 'ExamController',
controllerAs: 'examController',
},
// absolute naming targets the view defined above
'exam-pagination#exam':{
templateUrl: 'app/state-exam/exam-pagination.html'
},
}
})
.state('exam.instructions', {
url: '/instructions',
views: {
// 'exam-pagination':{}, // defined in parent
'exam-detail' : {
templateUrl: 'app/state-exam/exam-instructions.html'
}
}
})
.state('exam.math', {
url: '/math',
views: {
// 'exam-pagination':{}, // defined in parent
'exam-detail' : {
templateUrl: 'app/state-exam/exam-math.html'
}
}
});
Also check this to get more details about absolute view naming
Angular UI router nested views
Angular-UI Router: Nested Views Not Working
The working example is here
I have a UI-Router document set up to show the "pages" sections of a demo.
(function() {
'use strict';
angular.module('pb.ds.pages').config(function($stateProvider) {
$stateProvider.state('pages', {
abstract: true,
url: '/pages',
templateUrl: 'modules/pages/templates/pages.html',
controller: 'PagesController as pages',
data: {
pageTitle: 'Pages',
access: 'public',
bodyClass: 'pages'
}
})
.state('pages.signin', {
url: '/signin',
templateURL: 'modules/pages/templates/signin.html',
controller: 'SignInController as signin'
})
.state('pages.forgotpassword', {
url: '/forgotpassword',
templateURL: 'modules/pages/templates/forgotpassword.html',
controller: 'ForgotPasswordController as forgot'
})
.state('pages.404', {
url: '/404',
templateURL: 'modules/pages/templates/404.html',
controller: '404Controller'
});
});
})();
The parent state, "pages" has the ui-view on it, but otherwise I don't need to "show" it. I am only interested in showing its children, such as pages.signin or pages.forgotpassword.
Typing in the url "/forgotpassword" bounces me back to my homepage, which is the "otherwise" state in my app.module.js
// UI ROUTER CONFIG
angular.module('app').config(function($stateProvider) {
$stateProvider.state('otherwise', {
url: '*path',
template: '',
controller: function($state) {
$state.go('dashboard');
}
});
});
No errors in console, and all the pages in question are linked in my index.html.
I'm sure I must have missed something obvious. Any clues?
UPDATE
If I enter /pages/forgotpassword it does go to the correct path but the view is not being populated by the template...
There is a working plunker
We have to adjust state definition like this:
$stateProvider.state('pages', {
abstract: true,
//url: '/pages',
templateUrl: 'modules/pages/templates/pages.html',
controller: 'PagesController as pages',
data: {
pageTitle: 'Pages',
access: 'public',
bodyClass: 'pages'
}
})
.state('pages.signin', {
url: '/signin',
// templateURL: 'modules/pages/templates/signin.html',
templateUrl: 'modules/pages/templates/signin.html',
controller: 'SignInController as signin'
})
.state('pages.forgotpassword', {
url: '/forgotpassword',
//templateURL: 'modules/pages/templates/forgotpassword.html',
templateUrl: 'modules/pages/templates/forgotpassword.html',
controller: 'ForgotPasswordController as forgot'
})
The most important is replacement of the templateURL with templateUrl. Javascript (and UI-Router) is case sensitive.
We also do not need define url for parent... it could be just child state definition
Finally, we must be sure, that our parent contains some target ui-view="" where child states will be placed. E.g. this is the plunker pages.html:
<div>
<h3>pages</h3>
<hr />
<div ui-view=""></div>
</div>
These links will then work as expected:
//href
<a href="#/signin">
<a href="#/forgotpassword">
//ui-sref
<a ui-sref="pages.signin">
<a ui-sref="pages.forgotpassword">
We can leave the parent url:
$stateProvider.state('pages', {
abstract: true,
url: '/pages',
...
but the href links for child states must contain the parent url as well:
<a href="#/pages/signin">
<a href="#/pages/forgotpassword">
Check it here in action
You need to prefix the state url with the url of the parent state. So the correct url that you need open with the browser should be: #/pages/forgotpassword
Check the doc URL Routing for Nested States