Nested states in Ui-Router with AngularJS - javascript

I would like to have a common state with all the common views like the header and the sidebar, and a template where I would like to load different views that can change when the state is changing.
I have an index HTML file like this:
...
<div ui-view="header"></div>
<div ui-view="sidebar"></div>
<div class="container">
<div class="wrapper">
<div ui-view="content"></div>
</div>
</div>
...
While the AngularJS config is something like this:
$stateProvider
.state('mainCommonState', {
url: '',
abstract: true,
views: {
header: {
templateUrl: 'app/common/header.html',
controller: 'headerCtrl',
controllerAs: 'vm'
},
sidebar: {
templateUrl: 'app/common/sidebar.html',
controller: 'sidebarCtrl',
controllerAs: 'vm'
},
content: {}
},
resolve: {
apiEnvironment: function ($q, environmentApiService) {
var deferred = $q.defer();
deferred.resolve(environmentApiService.getApiEnvironment());
return deferred.promise;
}
}
})
.state('first-page-content', {
url: '/first-page-content',
parent: 'mainCommonState',
views: {
content: {
templateUrl: 'app/components/first-page-content.html'
controller: 'firstPageCtrl',
controllerAs: 'vm'
}
}
})
.state('second-page-content', {
url: '/second-page-content',
parent: 'mainCommonState',
views: {
content: {
templateUrl: 'app/components/second-page-content.html'
controller: 'secondPageCtrl',
controllerAs: 'vm'
}
}
})
.state('third-page-content', {
url: '/third-page-content',
parent: 'mainCommonState',
views: {
content: {
templateUrl: 'app/components/third-page-content.html'
controller: 'thirdPageCtrl',
controllerAs: 'vm'
}
}
})
For some reason this is not working: I have an empty view instead of the 3 templates that I would like to show in the content ui-view.
If I define a template (even a blank template) inside the the abstract state, the view that is always showing is the one inside the abstract state mainCommonState.
Where am I wrong?
1st Edit: UPDATE Following the first answer
Following the suggestion from Chris T, I have updated my code, but there still something missing.
I have created a Plunker so you can help me fixing the issues.
2nd Edit
Following the suggestions from Chris T, I have updated the code using the absolute path for the content view and now the contents are switching correctly.
I have updated the Plunker accordingly to that and introduced a new level of nesting view (tabs in the first page), and I would like to have the first tab active when the first page content is loaded.
If I follow these solutions and set empty the url of the first page and set it to the first tab instead, this is not working.
Any suggestions?

Your views are targeting the wrong named ui-view.
.state('second-page-content', {
url: '/second-page-content',
parent: 'mainCommonState',
views: {
content: {
templateUrl: 'app/components/second-page-content.html'
controller: 'secondPageCtrl',
controllerAs: 'vm'
}
}
})
In this snippet, it targets the ui-view named content from the parent state which is mainCommonState. However, the content ui-view was not created in the mainCommonState. It was created in the root template.
Change your view declarations to target the view at the correct state, for example this targets the content view at the root state (which is named empty string):
.state('second-page-content', {
url: '/second-page-content',
parent: 'mainCommonState',
views: {
'content#': {
templateUrl: 'app/components/second-page-content.html'
controller: 'secondPageCtrl',
controllerAs: 'vm'
}
}
})
In ui-router 1.0 and higher you can also use absolute ui-view names by prefixing with an exclamation
.state('second-page-content', {
url: '/second-page-content',
parent: 'mainCommonState',
views: {
'!content': {
templateUrl: 'app/components/second-page-content.html'
controller: 'secondPageCtrl',
controllerAs: 'vm'
}
}
})
Read more about view targeting in the UI-Router views guide:
https://ui-router.github.io/guide/views#view-name-only

Related

How to define multiple views with parent child state using UI router?

Is it possible to define multiple views in child state with parent child state relationship using UI-Router?
I have the following code in my config
$urlRouterProvider.otherwise("/child");
$stateProvider
.state('parent', {
abstract: true,
views: {
'parent': {
templateUrl: "parent.html",
controller: "parentCtrl as parentCtrl"
},
}
})
.state('parent.child', {
url: '/child',
views: {
'state1#parent.child': {
templateUrl: "child.html",
controller: "childCtrl as childCtrl"
},
}
});
I verify that my parent.html is showing up, but my child.html is not
If I move my child.html to the parent views object like
$stateProvider
.state('parent', {
abstract: true,
views: {
'parent': {
templateUrl: "parent.html",
controller: "parentCtrl as parentCtrl"
},
'state1#parent.child': {
templateUrl: "child.html",
controller: "childCtrl as childCtrl"
},
}
})
Than child.html works.
I verify using console.log($state.$current.name); in my parentCtrl that my current state is parent.child.
Can someone give me some hint?
Thanks
There is a working plunker
I adjusted your states and mostly child views : {} like this
$stateProvider
.state('parent', {
abstract: true,
views: {
'parent': {
templateUrl: "parent.html",
controller: "parentCtrl as parentCtrl"
},
}
})
.state('parent.child', {
url: '/child',
views: {
//'state1#parent.child': {
'view1#parent': {
templateUrl: "child.html",
controller: "childCtrl as childCtrl"
},
// the same as view2#parent
'view2': {
templateUrl: "child.html",
controller: "childCtrl as childCtrl"
},
'view3#': {
templateUrl: "child.html",
controller: "childCtrl as childCtrl"
},
}
the construct '...#parent.child' is wrong, because it is a absolute naming... trying to say, that we search a ui-view="..." inside of the 'parent.child' state. And that is not the case.
So, let's place this into index:
// place for parent
<div ui-view="parent"></div>
// place for child view number 3
place in index for view 3
<div ui-view="view3"></div>
And parent template could be like this
<div ui-view="view1"></div>
<div ui-view="view2"></div>
Then the above state def will target view1 with absolute naming, the same for view3 - being in index.html; view2 will go to parent with relative name
check it here

could not resolve state "home.app.detail" from state "home.apps.list", UI-Router

I have a page showing the list of applications that I want to be able to go to the page of the details of the app, when I click on each one of them.
Here is my config:
module bandar {
'use strict';
export class RouterConfig {
/** #ngInject */
constructor($stateProvider: ng.ui.IStateProvider,
$urlRouterProvider: ng.ui.IUrlRouterProvider,
$locationProvider: ng.ILocationProvider) {
$stateProvider
.state('home', {
url: '',
abstract: true,
templateUrl: 'app/components/main/main.html',
controller: 'MainController',
controllerAs: 'mainCtrl'
})
.state('home.apps', {
url: '/apps',
abstract: true,
templateUrl: 'app/components/apps/apps.html',
controller: 'AppsController',
controllerAs: 'appsCtrl',
})
.state('home.apps.list', {
url: '',
templateUrl: 'app/components/apps/list.html',
})
.state('home.app.detail', {
url: '/app/:package_name',
templateUrl: 'app/components/apps/app.html',
controller: 'AppController',
controllerAs: 'appCtrl',
});
$urlRouterProvider.otherwise('/apps');
/*$locationProvider.html5Mode(true).hashPrefix('');*/
}
}
}
And here is the part of the list template which is anchoring to the app's details page:
<a ui-sref="home.app.detail({package_name: app.package_name})">{{app.title}}</a>
But when I hit it in my browser, the following error occurs in the console:
Error: Could not resolve 'home.app.detail' from state 'home.apps.list'
at Object.transitionTo (angular-ui-router.js:3140)
at Object.go (angular-ui-router.js:3068)
at angular-ui-router.js:4181
at angular.js:17682
at completeOutstandingRequest (angular.js:5387)
at angular.js:5659
I guess the problem is UI-Router thinks that I'm pointing at the state relatively, but I wanna do it in the absolute way.
The problem is parent name 'home.app' instead of 'home.apps'
// wrong
.state('home.app.detail', { ...
// should be
.state('home.apps.detail', { ...
because parent is
.state('home.apps', { ...
EXTEND in case, that this should not be child of 'home.apps' we have to options
1) do not inherit at all
.state('detail', { ...
2) introduce the parent(s) which is(are) used in the dot-state-name-notation
// exists already
.state('home', { ...
// this parent must be declared to be used later
.state('home.app', {
// now we can use parent 'home.app' because it exists
.state('home.app.detail', {

angularjs ui router : nested views and nested states

I'm new to angular and I'm trying to understand nested views concept.
Based on the example provided in their documentation: https://github.com/angular-ui/ui-router/wiki/Multiple-Named-Views
//home.html
<body>
<div ui-view="header"></div>
<div ui-view="settings"></div>
<div ui-view="content"></div>
</body>
I have settings.html which has a check box. If it's checked it will load in the view(not named) the advanced settings template if not it will load the basic template
//settings.html
<input type="checkbox" ng-change="change()" ng-model="advancedSettings" />
<div ui-view></div>
so far I have defined something like this:
$stateProvider
.state('home', {
views: {
'header': {},
'settings': {
templateUrl: 'settings.html'
},
'content': {},
}
})
since I have 2 templates basicSettings.html and advancedSettings.html that I need to load in the view from settings.html based on that checkbox, I thought I have to declare something like this:
.state('settings#home.basic',(){
templateUrl: 'basicSettings.html'
});
but it's not working, instead I receive a lot of errors on console. How is the best way to implement this, without removing names from homepage views(header,settings,content), also how do I change the view based on the check box?
Thanks
There is a working plunker
Solution here could be with states defined like this:
$stateProvider
.state('home', {
abstract: true,
url: "/home",
views: {
'header': {
template: "This is HEADER"
},
'settings': {
templateUrl: 'settings.html',
controller: 'HomeCtrl',
},
'content': {
template: "This is CONTENT"
},
}
})
.state('home.basic', {
url: "/basic",
templateUrl: 'basicSettings.html'
})
.state('home.advanced', {
url: "/advanced",
templateUrl: 'advancedSettings.html'
})
we have parent state "home" and two children. These are triggered on change by 'HomeCtrl', e.g. like this:
.controller('HomeCtrl', ['$scope', '$state',
function($scope, $state) {
$scope.advancedSettings = false;
$scope.change = function(){
var childState = $scope.advancedSettings
? "home.advanced"
: "home.basic";
$state.go(childState);
}
}])
So, based on the setting, the view target "settings" and its ui-view="" (unnamed one) is filled with a child state - basic or advanced
Check it here

AngularJS, Nested UI-View

Could someone give a hint on ui-view?
I have one main ui-view and then two nested views, but for some reason these two are not getting loaded.
$stateProvider
.state('index',{
url:'',
templateUrl: './app/modules/main.html'
})
.state('index.feed',{
parent:'index',
templateUrl: './app/modules/feed.html'
})
.state('index.status',{
parent: 'index',
templateUrl: './app/modules/status.html'
});
The main page is loading but the two other views not
in my main.html I have:
<div class="voucher-display" ui-view="index.feed"></div>
<div class="feed" ui-view="index.feed"></div>
I just get an empty file.
The value you pass to ui-view in your html is not the name of the state, it should be the name of the view.
Is the html in feed.html and status.html supposed to replace or be displayed along with main.html? Either scenario will change the solution, but assuming you always want main.html, and want to display the feed.html and status.html where their respective nested views are selected, you'd do something like this:
.state('index', {
url: '/',
views: {
'main': {
templateUrl: './app/modules/main.html'
}
}
})
.state('index', {
views: {
'main': {
templateUrl: './app/modules/main.html',
},
'detail': {
templateUrl: './app/modules/feed.html'
}
}
})
.state('index.status', {
views: {
'main': {
templateUrl: './app/modules/main.html',
},
'detail': {
templateUrl: './app/modules/status.html'
}
}
});
And in your html you'd need two div tags that map the view.
<div ui-view="main"></div>
<div ui-view="detail"></div>
Now main.html should always show up in the first div, and feed.html or status.html, when their states are active, will show up in the second div.
Here is the docs for the ui-view directive, and here is a good blog post about ui-view.
After a couple of attempts, this worked to load the nested:
$stateProvider
.state('index', {
url: '',
views: {
'main': {
templateUrl: './app/modules/main.html'
},
'status#index': {
templateUrl: './app/modules/status.html'
},
'feed#index':{
templateUrl: './app/modules/feed.html'
}
}
})
index.html has:
<main ui-view="main"></main>
and inside the main.html I have:
<div class="voucher-display" ui-view="feed"></div>
<aside ui-view='status'></aside>
but now I got another problem, if I want to load another ui-view from my index.html, what should I do? because this does not work:
.state('clients',{
url:'/clients',
templateUrl: './app/modules/clients.html'
});

Angular js ui-router share controller

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.

Categories

Resources