How do I access other subviews in the same state. I am building a page with a toolbar on top and a sidebar and I want a button on the toolbar to open/close the sidebar and buttons in the sidebar to change the content. easy of it's all in the same controller but what I did is to use ui-router's subview feature like this:
.state('dash', {
url: '/dash/:id',
views: {
nav: {
controller: 'NavCtrl',
controllerAs: 'ctrl',
templateUrl: '/views/navbar.html'
},
sidebar: {
controller: 'SidebarCtrl',
controllerAs: 'ctrl',
templateUrl: '/views/sidebar.html'
},
content: {
controller: 'DashCtrl',
controllerAs: 'ctrl',
templateUrl: '/views/dash.html'
}
}
})
UI looks like this:
Define a resolve and use it as a place to store common data for the activated 'dash' state.
app.config(function($stateProvider) {
$stateProvider.state('dash', {
url: '/',
resolve: {
dashData: function() {
return { input: "default value" };
}
},
views: {
nav: {
controller: function() {
},
controllerAs: 'ctrl',
template: '<h3>This is the Navbar</h3>'
},
sidebar: {
controller: function(dashData) { // Inject reference to resolve object
this.dashData = dashData;
},
controllerAs: 'ctrl',
template: 'content data visible in ' +
'the sidebar: <b>{{ ctrl.dashData.input }}<b>'
},
content: {
controller: function(dashData) { // Inject reference to resolve object
this.dashData = dashData;
},
controllerAs: 'ctrl',
template: '<input type="text" ng-model="ctrl.dashData.input">' +
'This is bound to dashData.input'
}
}
})
});
Inject the shared object into each controller
app.controller('DashCtrl', function(dashData, $scope) {
$scope.dashData = dashData;
});
app.controller('... ....
I put this example in a plunker for you: http://plnkr.co/edit/8M1zXN0W5ybiB8KyxvqW?p=preview
This would be a good example of where an abstract parent state comes in handy:
An abstract state can have child states but can not get activated itself. An 'abstract' state is simply a state that can't be transitioned to. It is activated implicitly when one of its descendants are activated.
https://github.com/angular-ui/ui-router/wiki/Nested-States-and-Nested-Views#abstract-states
And then especially this usecase:
inherit $scope objects down to children
Consider the following abstract parent state and it's child state:
$stateProvider.state('root', {
abstract: true,
url: '/dash',
templateUrl: 'root.html',
controller: 'rootController'
});
$stateProvider.state('dash', {
parent: 'root',
url: '/:id',
views: {
'navbar': {
templateUrl: 'navbar.html',
controller: 'navbarController'
},
'sidebar': {
templateUrl: 'sidebar.html',
controller: 'sidebarController'
},
'content': {
templateUrl: 'content.html',
controller: 'contentController'
}
}
});
Now you can store logic (and data) you need in your childstate in the controller of the abstract parent state:
angular.module('app').controller('rootController', [
'$scope',
function ($scope) {
$scope.sidebar = {
show: true
};
$scope.items = [{
name: 'Alpha'
}, {
name: 'Bravo'
},{
name: 'Charlie'
},{
name: 'Delta'
}];
$scope.selected = $scope.items[0];
$scope.select = function (item) {
$scope.selected = item;
}
}
]);
Example of using this logic/data in a template of the child state, sidebar.html:
<ul class="nav nav-pills nav-stacked">
<li ng-repeat="item in items" role="presentation">
<a href="#" ng-click="select(item)">{{item.name}}</a>
</li>
</ul>
Here's a complete example with your requirements, i could post all the code here but i think that would be a bit too much:
http://embed.plnkr.co/2jKJtFM0GWsylyLcAdne/
I'll gladly answer any question you may have, just holler. Good luck!
if you'd name your controllers differently with controller as, you could use the NavCtrl in the sidebarCtrl's template. Maybe use some boolean value that exists on the NavCtrl, that decides what to show in the sidebar? (from the comment)
This should work, haven't tried it though.
.state('dash', {
url: '/dash/:id',
views: {
nav: {
controller: 'NavCtrl',
controllerAs: 'navCtrl',
templateUrl: '/views/navbar.html'
},
sidebar: {
controller: 'SidebarCtrl',
controllerAs: 'sidebarCtrl',
templateUrl: '/views/sidebar.html'
},
content: {
controller: 'DashCtrl',
controllerAs: 'dashCtrl',
templateUrl: '/views/dash.html'
}
}
})
sidebarService:
angular.module('app').value('sidebarService', {show: true});
navCtrl something like this:
function(sidebarService){
var vm = this;
vm.toggleSideBar = function(){sidebarService.show = !sidebarService.show;}//used in navbar.html
}
sidebarCtrl:
function(sidebarService){
var vm = this;
vm.showSideBar= sidebarService;
}
and then in sidebar.html you use the sidebar value service:
<div ng-if="sidebarCtrl.showSideBar.show">
<!--SideBar-->
</div
You can use events to communicate between controllers. Check the AngularJS documentation for $scope.$broadcast
and `$scope.$on : https://docs.angularjs.org/api/ng/type/$rootScope.Scope