i use $rootScope to send data to a controller from a nested state and view
It works between 'items' and rootSelector,
It does not work between 'detail' and rootSelector
something blocks my rootScope events.
var listState = {
name: 'playlist.list',
parent: 'playlist',
url: '/list?',
resolve: {
playlistsDb: resolvePlaylistsFromDb,
},
views: {
// target the unnamed view in the playlist state
'#playlist': {
templateUrl: 'app/playlists/list/list.html',
controller: 'PlaylistListController',
controllerAs: 'list'
},
// target the content view in the playlist.list state
'content#playlist.list': {
templateUrl: 'app/playlists/list/items/items.html',
controller: 'PlaylistItemsController',
controllerAs: 'items'
},
// target the rootSelector view in the playlist state
'rootSelector#playlist.list': {
templateUrl: 'app/selector/rootSelector.html',
controller: 'rootSelectorController',
controllerAs: 'rootSelector',
resolve: {
selectedPlaylistId: resolveSelectedPlaylistId,
}
}
}
};
I can send and receive events via $rootScope.broadcast and $scope.$on
from PlaylistItemsController "items" to rootSelector
but i can't receive them if they come from the nested "detailState"
var detailState = {
name: 'playlist.list.detail',
parent: 'playlist.list',
url: '/detail/:id',
views: {
'detail#playlist.list': {
templateUrl: 'app/playlists/list/detail/detail.html',
controller: 'PlaylistDetailController',
controllerAs: 'detail',
resolve: {
playlistDb: resolvePlaylistDb,
}
}
}
};
Ok, events didnt go through cause
$scope.$on('removeTrackFromPlaylist'), function (event, args) { }
was no code error in VS2015
Related
I am attempting to get multiple views to use the same controller. I've tried a couple of things so far, none seem to work. By "doesnt work" I mean the controller MapController isnt instantiated and the views cannot see the controller
1
$stateProvider.state(PageStateNames.COMPONENTS_LIVEMAP, {
url: "/components/vehicles/:vehicle/:panel",
views: {
"": {
controller: "MapController as vm"
},
"content#app": {
templateUrl: "....html"
},
"sidenav#app": {
templateUrl: "....html"
}
}
});
2
$stateProvider.state(PageStateNames.COMPONENTS_LIVEMAP, {
url: "/components/vehicles/:vehicle/:panel",
controller: "MapController as vm"
views: {
"content#app": {
templateUrl: "....html"
},
"sidenav#app": {
templateUrl: "....html"
}
}
});
Having looked at existing questions this should work. Have I missed something?
$stateProvider.state(PageStateNames.COMPONENTS_LIVEMAP, {
url: "/components/vehicles/:vehicle/:panel",
views: {
"": {
templateUrl: "......html",
controller: "MapController as vm"
},
"content#app": {
templateUrl: "....html",
controller: "MapController as vm"
},
"sidenav#app": {
templateUrl: "....html",
controller: "MapController as vm"
}
}
});
To use the same controller in a state, you can use child-parent nested state. For example :
$stateProvider.state('home', {
templateUrl: '....html',
controller: 'ParentController'
})
.state('home.livemap', { // << this state will use parent controller instance, this is the dot notation to make livemap a child state of home (more info here https://github.com/angular-ui/ui-router/wiki/Nested-States-and-Nested-Views
templateUrl: '....html'
});
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)
Here's what I have. I need categories to be available at HomeController and NavbarController.
$stateProvider
.state('master', {
abstract: true,
views: {
'navbar#': {
templateUrl: 'views/partials/navbar.default.html',
controller: 'NavbarController as nb',
}
},
resolve: {
categories: function(Category) {
return Category.getList({ depth: 0 });
},
}
})
.state('home', {
parent: 'master',
url: '/',
views: {
'main#': {
templateUrl: 'views/home/home.html',
controller: 'HomeController as hc',
}
},
resolve: {
deals: function(Deal) {
return Deal.getList();
},
}
});
According to the documentation
Child states will inherit resolved dependencies from parent state(s), which they can overwrite. You can then inject resolved dependencies into the controllers and resolve functions of child states.
$stateProvider.state('parent', {
resolve:{
resA: function(){
return {'value': 'A'};
}
},
controller: function($scope, resA){
$scope.resA = resA.value;
}})
.state('parent.child', {
resolve:{
resB: function(resA){
return {'value': resA.value + 'B'};
}
},
controller: function($scope, resA, resB){
$scope.resA2 = resA.value;
$scope.resB = resB.value;
}
It also states:
The resolve keyword MUST be relative to state not views (in case you use multiple views).
The resolve keys MUST be injected into the child states if you want to wait for the promises to be resolved before instantiating the children.
How to access other state scope variable from another state ctrl?
If in 'session.index.detail' state, I want to access leftPanel scope variable from rightPanel Ctrl is it possible? What I want to do is I have a form in rightPanel, when I click save, I want to add the new data object back to leftPanel list and refresh it after it is successfully added to the database.
.state('session', {
abstract: true,
templateUrl: '/schedule/_session.html'
})
.state('session.index', {
url: '/sessionmang',
views: {
'leftPanel#session': {
controller: 'SessionCtrl',
controllerAs: 'vm',
templateUrl: '/schedule/_sessionPanel.html'
},
'rightPanel#session': {
templateUrl: '/schedule/_sessionDetailPanel.html'
}
}
})
.state('session.index.detail', {
params: {
sessionObj: null,
sessionTypeObj: null
},
views: {
'rightPanel#session': {
controller: 'SessionDetailCtrl',
controllerAs: 'vm',
templateUrl: '/schedule/_sessionDetailPanel.html',
resolve: {
sessionObj: ['$stateParams', function ($stateParams) {
return $stateParams.sessionObj;
}],
sessionTypeObj: ['$stateParams', function ($stateParams) {
return $stateParams.sessionTypeObj;
}]
}
}
}
});
I think the best way is to create a service that holds the data you want to share.
And share it between the controllers.
Or use broadcasting to talk between the controllers and pass data over that way
the simplest way is, you could use events like this.
when you click save, you will do
$scope.$broadcast('myevent', data);
from the rightCtrl and from the leftCtrl you could do
$scope.$on('myevent', function(event,data) {
//do your updates here
})
How can I get a param from child?
This is what I do:
$state.go('demo.content', {proces: '10'})
And then in controller 'Main' I need the value of param proces.
.state('demo', {
url: '/',
abstract: true,
views: {
'main': {
templateUrl: './App/main.html',
controller: 'Main'
}
})
.state('demo.content', {
url: '/monitoring',
views: {
'chart': {
templateUrl: './App/chart.html',
controller: 'Chart'
},
'grid': {
templateUrl: './App/grid.html',
controller: 'Grid'
},
params: {proces: '4'}
})
The point here is, that any call to 'demo.content' with different param process should re-call/re-init the controller of such state view.
Other words, only controllers which are part of this state, could consume the process as a part of injected params.
So, if there is really any part in main controller, which needs handle (changing) process id, it should be moved to the demo.content state, as another view
If this is the true, the best we can do is to move that view from parent into state:
.state('demo.content', {
url: '/monitoring',
views: {
'chart': {
templateUrl: './App/chart.html',
controller: 'Chart'
},
'grid': {
templateUrl: './App/grid.html',
controller: 'Grid'
},
// this would do the stuff fro main controller
// based on changing params
'mainProcessor': {
templateUrl: './App/mainProcessor.html',
controller: 'mainProcessor'
}
This, and only this way, you can be sure, that different param value will trigger the "mainProcessor" controller and allow him to do the job