UI router - Multiple nested named views in a single state - angularjs

index.html
--navbar.html
--content.html
--customer.html
--netScore.html
--useExp.html
--useExpNest1.html
--useExpNest2.html
--internalPerformance.html
--leftNavPanel.html
I have this kind of view structure and I want to load them all at once so I'm planning to put this in a single state. I saw this answer but it seems that its only applicable for a simple/double nested views(I have 3 or more nested views). How can I put this in a single state, or is there a better way if not possible?
EDIT
I've come up with this solution and it works somehow.
.state('index', {
url: '/',
views: {
'': {
templateUrl: 'app/modules/bulletin/views/index.view.html',
controller: 'indexController'
},
'navbar#index': {
templateUrl: 'app/modules/bulletin/views/index/navbar.view.html',
controller: 'navbarController'
},
'content#index': {
templateUrl: 'app/modules/bulletin/views/index/content.view.html',
controller: 'contentController'
},
'leftNavPanel#index': {
templateUrl: 'app/modules/bulletin/views/index/leftNavPanel.view.html',
controller: 'contentController'
}
}
})
.state('index.content', {
views: {
'customer#index': {
templateUrl: 'app/modules/bulletin/views/index/content/customer.view.html'
},
'internalPerformance#index': {
templateUrl: 'app/modules/bulletin/views/index/content/internalPerformance.view.html'
}
}
})
.state('index.content.customer', {
views: {
'netScore#index.content': {
templateUrl: 'app/modules/bulletin/views/index/content/customer/netScore.view.html'
},
'useExp#index.content': {
templateUrl: 'app/modules/bulletin/views/index/content/customer/useExp.view.html'
}
}
})
.state('index.content.customer.useExp', {
views: {
'useExpNest1#index.content.customer': {
templateUrl: 'app/modules/bulletin/views/index/content/customer/useExp/useExpNest1.view.html'
},
'useExpNest2#index.content.customer': {
templateUrl: 'app/modules/bulletin/views/index/content/customer/useExp/useExpNest2.view.html'
}
}
})
And then add this code to the indexController(most parent controller)
$state.go('index.content');
$state.go('index.content.customer');
$state.go('index.content.customer.useExp');
But this answer is still wrong because, let's say that netScore.html has some child views, we will create route for it then go to that state, but netScore and useExp states are on the same level so only one of them will be loaded if we use
$state.go('index.content');
$state.go('index.content.customer');
$state.go('index.content.customer.netScore');
$state.go('index.content.customer.useExp');
EDIT 2
Here's a plunker of what I've done so far. The view names are slightly different but you will see clearly the problem there

You can use a combination of named views plus abstract: true property to load child views by default
angular.module('sampleModule', [
'ui.router'
]);
angular.module('sampleModule')
.config(['$stateProvider', '$urlRouterProvider', function($stateProvider, $urlRouterProvider) {
$urlRouterProvider.when('','/');
$stateProvider
.state('main', {
url: '',
abstract: true,
templateUrl: 'main.view.html'
})
.state('main.load', {
url: '/',
abstract: true,
views:{
'content':{
templateUrl:'content.view.html',
},
'navbar':{
templateUrl:'navbar.view.html',
}
}
})
.state('main.load.customer', {
url: '',
abstract: true,
views:{
'customerPerception':{
templateUrl:'content-customerPerception.view.html'
},
'customerExperience':{
templateUrl:'content-customerExperience.view.html'
}
}
})
.state('main.load.customer.netTrustScore', {
url: '',
abstract: true,
views: {
'netTrustScore': {
templateUrl: 'content-customerPerception-netTrustScore.view.html'
},
'useExperience': {
templateUrl: 'content-customerPerception-useExperience.view.html'
},
'trustStatements': {
templateUrl: 'content-customerPerception-trustStatements.view.html'
}
}
})
.state('main.load.customer.netTrustScore.somethingElse', {
url: '',
views: {
'abc': {
templateUrl: 'content-customerExperience-customerComplaints.view.html'
},
'': {
templateUrl: 'content-customerExperience-networkQualityIndex.view.html'
}
}
})
;
}])
.controller('mainController', ['$scope', '$state', function($scope, $state) {
console.log('mainController initialized!');
}]);
here's a plnkr
https://plnkr.co/edit/BBAeWjnGbTsbO1lMguU9?p=preview

Thanks to the guys from AngularJS group in FB. The problem is I put two sibling views in two different states. UI router cant load two states at the same time. So the solution is to put all same level views in a single subState.
Lets assume we have this kind of structure:
index.html
--navbar.html
--content.html
--customer.html
--netScore.html
--netScoreNest1.html
--netScoreNest2.html
--useExp.html
--useExpNest1.html
--useExpNest2.html
--internalPerformance.html
--leftNavPanel.html
the proper routing for this would be like this
.state('index', {
url: '/',
views: {
'': {
templateUrl: 'index.view.html',
controller: 'mainController'
},
'navbar#main': {
templateUrl: 'index/navbar.view.html'
},
'content#main': {
templateUrl: 'index/content.view.html'
},
'leftNavPanel#main': {
templateUrl: 'index/leftNavPanel.view.html'
}
}
})
.state('index.subLevel', {
views: {
'customer#index': {
templateUrl: 'index/content/customer.view.html'
},
'internalPerformance#index': {
templateUrl: 'index/content/internalPerformance.view.html'
}
// IF LEFTNAVPANEL OR NAVBAR HAVE SUB VIEWS, PUT IT HERE
}
})
.state('index.subLevel.subLevel2', {
views: {
'netScore#index.subLevel': {
templateUrl: 'index/content/customer/netScore.view.html'
},
'useExp#index.subLevel': {
templateUrl: 'index/content/customer/useExp.view.html'
}
// IF INTERNALPERFORMANCE HAVE SUB VIEWS, PUT IT HERE
}
})
.state('index.subLevel.subLevel2.subLevel3', {
views: {
'netScoreNest1#index.subLevel.subLevel2': {
templateUrl: 'index/content/customer/netScore/netScoreNest1.view.html'
},
'netScoreNest2#index.subLevel.subLevel2': {
templateUrl: 'index/content/customer/netScore/netScoreNest2.view.html'
},
'useExpNest1#index.subLevel.subLevel2': {
templateUrl: 'index/content/customer/useExp/useExpNest1.view.html'
},
'useExpNest2#index.subLevel.subLevel2': {
templateUrl: 'index/content/customer/useExp/useExpNest2.view.html'
}
}
})
And then in mainController, load the inner most child state, this will automatically load the views of all its parent(up to topmost parent state 'index')
$state.go('index.subLevel.subLevel2.subLevel3');
And thats it. And also here's a plunker to make it easier to understand. (Views and structure are slightly different from this post different. Too lazy to edit)

Related

Cannot load ui-router nested state page

I'm trying to make main.marketing-groups.detail a nested state of main.marketing-groups however when I'm calling $state.go('main.marketing-groups.detail'); all I am getting is the url change to .../marketing-groups/detail but the HTML persists from the parent. Put some debug console.log into the marketingGroupsDetailController but it looks like it's not loaded. Both controllers do exist in index.html and are properly loaded.
.state('main.marketing-groups', {
url: '/marketing-groups',
views: {
'content': {
templateUrl: 'app/modules/marketing/groups/marketing-groups.tpl.html',
controller: 'marketingGroupsController as vm'
},
'right-drawer#main': {}
}
})
.state('main.marketing-groups.detail', {
url: '/detail',
views: {
'content': {
templateUrl: 'app/modules/marketing/groups/detail/marketing-groups-detail.tpl.html',
controller: 'marketingGroupsDetailController as vm'
},
'right-drawer#main': {}
}
})
What might wrong with it as I'm changing the code bit by bit but nothing works. I am trying to avoid using ui-view this time.
No console.log errors either.
I've made it work with this approach. If you've got something better, please let me know.
.state('main.marketing', {
url: '/marketing',
views: {
'content': {
templateUrl: 'app/modules/marketing/marketing.tpl.html',
controller: 'marketingController as vm'
},
'right-drawer#main': {}
}
})
.state('main.marketing.groups', {
url: '/groups',
views: {
'marketing#main.marketing': {
templateUrl: 'app/modules/marketing/groups/marketing-groups.tpl.html',
controller: 'marketingGroupsController as vm'
},
'right-drawer#main': {}
}
})
.state('main.marketing.groups.detail', {
url: '/detail',
views: {
'marketing#main.marketing': {
templateUrl: 'app/modules/marketing/groups/detail/marketing-groups-detail.tpl.html',
controller: 'marketingGroupsDetailController as vm'
},
'right-drawer#main': {}
}
})
marketing.tpl.html
<div ui-view="marketing"></div>

Set a defaults for views in Ionic AngularJS

I'm learning Ionic framefork to develop an app.
I defined an abstract state like this:
.state('app', {
url: '',
abstract: true,
templateUrl: "templates/default.html"
})
My default.html looks like this:
<ion-side-menus>
<ion-side-menu-content>
<div ui-view="headerview"></div>
<div ui-view="contentview"></div>
</ion-side-menu-content>
<div ui-view="menuview"></div>
And then I declared all my actual states
.state('app.contacts', {
url: '/contacts',
views: {
'headerview': {
templateUrl: "templates/common/header.html" ,
controller: 'headerControllers'
},
'menuview': {
templateUrl: "templates/common/menu.html"
},
'contentview': {
templateUrl: 'templates/contacts.html',
controller: 'contactControllers'
}
}
})
.state('app.partners', {
url: '/partners',
views: {
'headerview': {
templateUrl: "templates/common/header.html" ,
controller: 'headerControllers'
},
'menuview': {
templateUrl: "templates/common/menu.html"
},
'contentview': {
templateUrl: 'templates/partners.html',
controller: 'partnerControllers'
}
}
})
This works like a charm, but I noted that header and menu views will be the same in almost all of my states! That's not very DRY. There is a way to define a default that I can overwrite when needed?
Thank you,
one way to do this is to create store your path in a variable and use it for all your state.
in the begining of your function make :
var defaultHeader = 'templates/common/header.html';
var defaultHeaderController = 'headerControllers';
var defaultMenu = 'templates/common/menu.html';
then during the init of your state you will be able to do :
.state('app.contacts', {
url: '/contacts',
views: {
'headerview': {
templateUrl: defaultHeader ,
controller: defaultHeaderController
},
'menuview': {
templateUrl: defaultMenu
},
'contentview': {
templateUrl: 'templates/contacts.html',
controller: 'contactControllers'
}
}
})
.state('app.partners', {
url: '/partners',
views: {
'headerview': {
templateUrl: defaultHeader ,
controller: defaultHeaderController
},
'menuview': {
templateUrl: defaultMenu
},
'contentview': {
templateUrl: 'templates/partners.html',
controller: 'partnerControllers'
}
}
})
Like that if you want to overwrite the default view you can.
And to update it you have only one place to modify the code.

switching layouts with angular ui-router

I try to use ui-router for switching between different layouts
$urlRouterProvider.otherwise('/');
$stateProvider
.state('root', {
url: '/',
views: {
'#': {
templateUrl: '_columnsTwo.html' // 2 columns
},
'main#root': {
templateUrl: 'content1.html',
controller: 'homeCtrl'
}
}
})
.state('data', {
url: '/du-lieu',
parent: 'root',
views: {
'main': {
templateUrl: 'content2.html',
controller: 'dataCtrl'
}
}
})
.state('oneCol', {
url: '/chi-tiet-tin',
views: {
'#': {
templateUrl: '_columnsOne.html' // one column layout
},
'detail#oneCol': {
templateUrl: 'contentOneCol.html'
}
}
})
});
Full code is at http://plnkr.co/edit/wGU6PaPAloCm33TDoGso?p=preview
Dont know why the state oneCol doesn't work.
In this case, it is usually a typo, Check this updated plunker: http://plnkr.co/edit/GygQZxEHogqcRS8vf1Bc?p=preview
The templateUrl: 'contentOneCol.html' was calling a template - which did not exist:
.state('oneCol', {
url: '/chi-tiet-tin',
views: {
'#': {
templateUrl: '_columnsOne.html' // one column layout
},
'detail#oneCol': {
templateUrl: 'contentOneCol.html' // this file was not there
} // there was one with suffix '.hml'
}
})
Because the file in the plunker was named
contentOneCol.hml // missing t
contentOneCol.html // correction I made

Same html page for different tabs in Angular UI Router

My angular app is routed as following:
$stateProvider
// setup an abstract state for the tabs directive
.state('tab', {
url: "/tab",
abstract: true,
templateUrl: "templates/tabs.html"
})
// Each tab has its own nav history stack:
.state('tab.dotnet', {
url: '/dotnet',
views: {
'tab-dotnet': {
templateUrl: 'templates/tab-dotnet.html',
controller: 'QuestionsCtrl'
}
}
})
.state('tab.sql', {
url: '/sql',
views: {
'tab-sql': {
templateUrl: 'templates/tab-sql.html',
controller: 'QuestionsCtrl'
}
}
})
The above two routes use the same controller but different html pages.
Since both the pages are same, I want to have a single html page in my application instead of two different tab-sql and tab-dotnet pages.
But I will need a differentiation variable to be injected to the controller when selecting the tabs.
Basically I need something like this:
.state('tab.dotnet', {
url: '/dotnet',
views: {
'tab-dotnet': {
templateUrl: 'templates/tab.html',
controller: 'QuestionsCtrl',
type: 'dotnet' // so that i get this type in my Controller
}
}
})
.state('tab.sql', {
url: '/sql',
views: {
'tab-sql': {
templateUrl: 'templates/tab.html',
controller: 'QuestionsCtrl',
type: 'sql'
}
}
})
How to achieve this?
You can pass data to controllers in a state using resolve.
.state('tab.dotnet', {
url: '/dotnet',
views: {
'tab-dotnet': {
templateUrl: 'templates/tab.html',
controller: 'QuestionsCtrl',
resolve: {
type: 'dotnet';
}
}
}
})
.state('tab.sql', {
url: '/sql',
views: {
'tab-sql': {
templateUrl: 'templates/tab.html',
controller: 'QuestionsCtrl',
resolve: {
type: 'sql';
}
}
}
})
https://github.com/angular-ui/ui-router/wiki#resolve

Angular: inheriting view in states

In Angular/Ionic i have three different states:
.config(function($stateProvider, $urlRouterProvider) {
$stateProvider
.state('layout', {
url: "",
abstract: true,
templateUrl: "templates/base/layout.html"
})
.state('layout.home', {
url: "/home",
views: {
'mainContent' :{
templateUrl: "templates/home.html"
},
'leftMenuContent' :{
controller: "LeftMenuController",
templateUrl: "templates/left-menu.html"
}
}
})
.state('layout.about', {
url: "/about",
views: {
'mainContent' :{
templateUrl: "templates/about.html",
controller: "AboutController"
},
'leftMenuContent' :{
controller: "LeftMenuController",
templateUrl: "templates/left-menu.html"
}
}
})
So i have an abstract state which defines the base/main layout, home and about states which have two views: mainContent and leftMenuContent. As for the LeftMenuController functionality, it just returns custom options for every route depending the $state.current.name.
As leftMenuContent will be the same for every state, is there any way to avoid defining it in every new state and inheriting it from the layout state (or somewhere else) instead?
You are almost there: By calling the state layout.home it will inherit its views from layout, so this should work:
.state('layout', {
url: "",
abstract: true,
templateUrl: "templates/base/layout.html",
views: {
'leftMenuContent' :{
controller: "LeftMenuController",
templateUrl: "templates/left-menu.html"
}
}
})
.state('layout.home', {
url: "/home",
views: {
'mainContent' :{
templateUrl: "templates/home.html"
}
}
})
.state('layout.about', {
url: "/about",
views: {
'mainContent' :{
templateUrl: "templates/about.html",
controller: "AboutController"
}
}
})
This is what I found to be working to show nested views.
So, I have a main view in which I display my home template (<ui-view> in index.html), in the home template I have another <ui-view> in which I call different states based on what I want.
config(function config($stateProvider) {
$stateProvider
.state('sidemenu.home', {
url: '/home',
views: {
'topView': {
controller: 'HomeCtrl',
templateUrl: 'home/home.tpl.html'
}
}
})
.state('sidemenu.home.list', {
templateUrl: 'home/home-list.tpl.html'
/*,controller: 'HomeCtrl'*/
})
.state('sidemenu.home.login', {
templateUrl: 'home/home-login.tpl.html'
/*,controller: 'HomeCtrl'*/
})
.state('sidemenu.home.register', {
templateUrl: 'home/home-register.tpl.html'
/*,controller: 'HomeCtrl'*/
})
.state('sidemenu.home.coach', {
templateUrl: 'home/home-coach.tpl.html'
/*,controller: 'HomeCtrl'*/
})
;
})
.controller('HomeCtrl', function HomeController($scope, $rootScope, $state) {
/**
* ===== FIRST LOGIC =====
**/
//Redirect the user to the appropriate page.
switch ($rootScope.tellState()) {
case "ok-local":
$state.transitionTo('sidemenu.home.list');
break;
case "register":
$state.transitionTo('sidemenu.home.register');
break;
case "login":
$state.transitionTo('sidemenu.home.login');
break;
case "coach":
$state.transitionTo('sidemenu.home.coach');
break;
}

Resources