I have been reading about nested views and multiple views but I can't find an example using both
In a landing page I have multiple views, one after other. A picture speaks a thousand words:
To consider:
Every section/view will have full window height, so on scroll I want to change location to /view1, /view2, /view3, etc.
That should be compatible with going to /view1/b or /view3/b and showing subview (view1.b or view3.b).
Scroll should not make load page again.
I have success doing tasks separately but not all together.
you can use $statePorivder nested view with
config(function ($stateProvider) {
$stateProvider
.state('main', {
url: '/',
views: {
'': {
templateUrl: 'app/main/layout.html',
controller: 'LayoutController'
},
'header#main': {
templateUrl: 'app/main/header/layout.html',
controller: require("./headerController.js")
},
'left#main': {
templateUrl: 'app/main/left/layout.html',
controller: require("./leftController.js")
},
'content#main': {
templateUrl: 'app/main/content/layout.html',
controller: require("./conentController.js")
},
'right#main': {
templateUrl: 'app/main/right/layout.html',
controller: require("./rightController.js")
}
}
});
};
Related
I have seen a design interface here, and I liked it.
I was doing it with AngularJS, and ui-router which allows to have multiple views on a single page.
I have an issue for smartphone, because I want to display only one view, not two like tablets/desktops.
Question :
What would be the best way to display only one view for small screens, and two for larger screens ?
I had basic ideas :
Idea 1 (not so so good) : I was thinking to create multiple routes, then route to one or the other route depending the screen size.
Inside angular config
app.config(function($stateProvider) {
$stateProvider
.state('listForLargeScreens', {
url: "/listForLargeScreens",
views: {
"primary": { templateUrl: "list.html" },
"secondary": { templateUrl: "detail.html" }
}
})
.state('listForSmallScreens', {
url: "/listForSmallScreens",
views: {
"primary": { templateUrl: "list.html" }
}
})
...;
Inside a random controller
app.controller('RandomCtrl', function ($scope, $state) {
$scope.changePage = function () {
if (isLargeScreen) {
$state.go('indexForLargeScreens');
} else {
$state.go('indexForSmallScreens');
}
}
});
Problem : this idea seems to me too "dirty" for maintaining.
Idea 2 : I was thinking to declare primary and secondary views, then I could hide with CSS the secondary one with CSS :
Inside angular config
app.config(function($stateProvider) {
$stateProvider
.state('list', {
url: "/",
views: {
"primary": { templateUrl: "list.html" },
"secondary": { templateUrl: "detail.html" }
}
})
.state('detail', {
url: "/detail/:id",
views: {
"secondary": { templateUrl: "list.html" },
"primary": { templateUrl: "detail.html" }
}
})
...;
Inside html
<div ui-view="main"></div>
<div ui-view="secondary"></div> <!-- For small screens, hide this view with CSS -->
Problem : different URLS can have the same views, but with inverted priorities, see :
So, on small screens, it could work, but on larger screens, in some URLs, views can be inverted, (see the image above), so in HTML, list.html and detail.html templates will be inverted too.
It could solve the problem if I could invert <div ui-view="primary"></div> and <div ui-view="secondary"></div> on some views...
I've just found on scotch.io a piece of code which is the key for that problem.
The idea 2 on the question was almost the answer, but I had a problem with different URLs which where using same views, but with inverted priorities.
ui-router allows with multiple named views, to get ancestors, so I have to create a piece of html which declares ui-views :
Inside angular config :
$stateProvider.state('list', {
url: '/list',
views: {
'': { templateUrl: 'partial-list.html' },
'primary#list': {
templateUrl: 'list.html',
controller: 'ListCtrl as ctrl'
},
'secondary#list': {
templateUrl: 'detail.html',
controller: 'DetailCtrl as ctrl'
}
}
}).state('detail', {
url: '/detail/:id',
views: {
'': { templateUrl: 'partial-detail.html' },
'primary#list': {
templateUrl: 'detail.html',
controller: 'DetailCtrl as ctrl'
},
'secondary#list': {
templateUrl: 'list.html',
controller: 'ListCtrl as ctrl'
}
}
});
Inside partial-list.html :
<div ui-view="primary"></div>
<div ui-view="secondary" class="hide-it-for-small-screens"></div>
Inside partial-detail.html :
<div ui-view="secondary" class="hide-it-for-small-screens"></div>
<div ui-view="primary"></div>
It works ! :)
I have a state that has multiple views declared in it as follows:
$stateProvider
.state('home.details.item', {
url: '^/details',
views: {
'chartsView': {
templateUrl: 'charts.html',
controller: 'chartsCtrl'
},
'gridView': {
templateUrl: 'grid.html',
controller: 'gridCtrl'
},
'detailsView': {
templateUrl: 'details.html',
controller: 'detailsCtrl'
}
}
});
I need to reload one of the views without reloading the whole state, without using $state.go($state.current,null , {reload: true}) , and if possible, from the chartCtrl reload detailsCtrl. Is that possible?
I'd say, that the UI-Router solution should be built arround *states*, not views.
(I created working example here). Other words, if there are
some views which should not be reloaded and
some other views, which should be reloaded
... it calls for state nesting. Let's move that view into child state:
.state('home.details.item', {
url: '^/details',
views: {
'chartsView': {
templateUrl: 'tpl.charts.html',
controller: 'chartsCtrl'
},
'gridView': {
templateUrl: 'tpl.grid.html',
controller: 'gridCtrl'
},
// 'detailsView': {
// templateUrl: 'details.html',
// controller: 'detailsCtrl'
// }
}
})
.state('home.details.item.more', {
views: {
'detailsView#home.details': {
templateUrl: 'tpl.details.html',
controller: 'detailsCtrl'
}
}
})
We also need a state, which will do the reload. We could use other way, e.g. with some changing parameter in state more, but that would mean to change the param value on each call. With this specil state, we can easily reload our state 'more':
.state('reload', {
parent: "home.details.item",
views: {
'detailsView#home.details': {
// this controller will just redirect to 'more' and make it fresh...
controller: ['$state', function($state) { $state.go('^.more')}],
}
}
})
And with these simple controllers we can do all that required stuff:
.controller('chartsCtrl', function ($scope, $state) {
var childName = ".more";
$state.go(childName); // default is a sub state 'more'
})
.controller('detailsCtrl', function ($scope) {
$scope.when = Date.now();
})
Having this: we can call this to reload just details:
<a ui-sref="reload">force reload detail view</a>
Now, when navigating to reload, we will be redirected to state "more" and our view will be rerendered.
SUMMARY:
In general, UI-Router represents state machine. I would strongly suggest:
Do not worry to think in states. Views are just their representation in the DOM.
If there are some features related, they most likely represent state. If others do not relate (should be changed often or rarely) they belong to other state. It could be parent, child or sibling...
Check it here
I want to give 2 parts of my UI the same controller but still let them have each of their own unique controllers.
$stateProvider
.state('standard.page', {
url: '/:page',
resolve: {
page: function($stateParams) {
...
},
},
views: {
'content': {
templateUrl: '/tmpl/page',
controller: 'controllercontent'
},
'sideMenu': {
templateUrl: '/tmpl/menu',
controller: 'controllermenu',
}
}
})
So I want both content and sideMenu to share a controller. If I add a controller above the views then it requires a new template, I want to use the standard template instead of making a unique template for this state. Any ideas how I can get 3 controllers going in this example? Thanks.
I battled with this at some point in time, and I believe I made a template file that isn't directly accessible (via abstract: true). Here's an example...
.state('standard', {
url: '/standard',
abstract: true,
templateUrl: '/tmpl/standard.html',
controller: 'SharedController'
},
})
.state('standard.page', {
url: '/:page',
resolve: {
page: function($stateParams) {
...
},
},
views: {
'content': {
templateUrl: '/tmpl/page',
controller: 'controllercontent'
},
'sideMenu': {
templateUrl: '/tmpl/menu',
controller: 'controllermenu',
}
}
});
In your tmpl/standard.html file, make sure this exists somewhere within the file:
<div ui-view="sideMenu">
<div ui-view="content">
Hope this points you in the right direction.
I have an Angular application that depends on Angular ui-router. This application has multiple pages which share a common template such as the navbar:
var app = angular.module('app', ['ngSanitize', 'ngResource', 'ngRoute', 'ui.router'])
.config(['$urlRouterProvider', '$stateProvider', ($urlRouterProvider, $stateProvider) => {
$urlRouterProvider.otherwise("/index");
$stateProvider
.state('index', {
url: "/index",
views: {
'navbar': {
templateUrl: 'Views/Partials/navbar.cshtml',
controller: 'App.Controllers.NavbarController'
},
'content': {
templateUrl: 'Views/index.cshtml',
controller: 'App.Controllers.IndexController'
}
}
})
.state('settings', {
url: "/settings",
views: {
'navbar': {
templateUrl: 'Views/Partials/navbar.cshtml',
controller: 'App.Controllers.NavbarController'
},
'content': {
templateUrl: 'Views/settings.cshtml',
controller: 'App.Controllers.SettingsController'
}
}
});
}]);
Both '/index' and '/settings' share the same template 'Views/Partials/navbar.cshtml'. Upon testing, i found out, that every time a "page" is loaded for an url, all the views in it are reloaded.
Is it possible to avoid reloading the navbar, if it has been previously loaded already?
You should be able to extract the navbar into a parent state of your existing states. This way the navbar only loads when the parent state is entered and you should be able to change child states that share this parent without affecting it.
While there are better organised ways to do this, my quick and dirty way would be to rename the states you have to withnav.index and withnav.settings. Then remove the navbar view from them and add the following state.
$stateProvider
.state('withnav', {
abstract: true,
views: {
'navbar': {
templateUrl: 'Views/Partials/navbar.cshtml',
controller: 'App.Controllers.NavbarController'
}
}
});
WikiApp.config(function config($stateProvider, $urlRouterProvider) {
$stateProvider
.state('revision', {
url: '/wiki',
views: {
"main": {
controller: 'ListCtrl',
templateUrl: 'wiki/wiki.tpl.html'
},
"sidebar-left": {
templateUrl: 'wiki/wiki.sidebar-left.tpl.html'
}
},
data:{ pageTitle: 'List articles' }
})
This is what my Angular bit looks like and this is how I execute it inside of a template (wiki.tpl.html):
<div ui-view="sidebar-left"></div>
Now the main view works fine, but as I try to integrate the sidebar, it doesn't load, what am I doing wrong and how can I use more than one template in a single page like this?
Thank you!
WikiApp.config(function config($stateProvider, $urlRouterProvider) {
$stateProvider
.state('revision', {
url: '/wiki',
views: {
main: {
controller: 'ListCtrl',
templateUrl: 'wiki/wiki.tpl.html'
},
sidebarLeft: {
templateUrl: 'wiki/wiki.sidebar-left.tpl.html'
}
},
data:{ pageTitle: 'List articles' }
})
If you want to use nested templates you should implement that using sub-views. In your current example you are setting both templates as sibling templates.
I suggest you to create 2 states. Abstract view for the main template 'main' and another view 'main.wiki'. Route should be assigned to 'main.wiki' state ant it will inherit parameters from the main view (including template settings).
Hope that's clear.