I am using UI router to define several states and their child views. I set their controllers accordingly as well.
I'd like one or more of those controllers to be active before the view is loaded, because of data fetching and some other operations.
Is this possible? Is this a good/bad idea?
.state(stateNames.MY_STATE, {
url: '/products/:view',
views: {
'items-of-count#my.state': {
templateUrl: 'path/to/template.html',
controller: 'NotYetLoadedController'
},
'selected-items-count#my.state': {
templateUrl: 'path/to/this/template.html',
controller: 'NotYetLoadedController'
}
},
params: {
view: {
squash: true
}
}
})
Related
I'm having problems loading my child states on an abstract true parent state.
This is my parent state
.state('main', {
url: '/main',
templateUrl: 'templates/main.html',
abstract: true
})
This is the child states
.state('main.panels', {
views: {
'ticketsPanel': {
templateUrl: 'templates/ticketsPanel.html'
},
'productsPanel': {
templateUrl: 'templates/productsPanel.html'
},
'categoriesPanel': {
templateUrl: 'templates/categoriesPanel.html'
}
}
})
I have a login page after I login I want to load all 3 child views.
This is the code that process the login.
.controller('loginController', function($scope, Authentication, $log, $state){
$scope.formData = {};
$scope.processForm = function(){
Authentication.login($scope.formData);
var promise = Authentication.getEmployee();
promise.then(function(respond){
localStorage.setItem('employee', JSON.stringify(respond));
$state.go('main.panels');
})
}
})
The $state.go('main.panels') activates the child state of the main state parent, but the problem I'm having is that DOM is showing the element have being loaded but I can only see them partially in my view. It's like they didn't got fully loaded.
My question is how can I wait for all the views in the main.panels to be loaded completely before I transition to that view.
We do have 'resolve' property that can be provided in the definition of each and every state (or view) whatever you want to load. So what angular-ui-router does is that it resolves the 'resolve' property first and only then the HTML template is being rendered on the browser.
You can define the child state in the following way :
.state('main.panels', {
views: {
'ticketsPanel': {
templateUrl: 'templates/ticketsPanel.html',
resolve: function(LoginService){return LoginService};
},
'productsPanel': {
templateUrl: 'templates/productsPanel.html',
resolve: function(LoginService){return LoginService};
},
'categoriesPanel': {
templateUrl: 'templates/categoriesPanel.html',
resolve: function(LoginService){return LoginService};
}
}
})
You can even read the following links for more details :
https://github.com/angular-ui/ui-router/wiki
https://github.com/angular-ui/ui-router/wiki/Nested-States-%26-Nested-Views#inherited-resolved-dependencies
http://www.jvandemo.com/how-to-resolve-angularjs-resources-with-ui-router/
It has been explained in detail. Hope this helps :)
nested states also makes the views nested, meaning the router will look for a named ui-view within the parent template and render it there, and since that's not what you're trying to do you have to implicitly state that it's the parent view (absolute vs relative) like so:
.state('main.panels', {
views: {
'ticketsPanel#': {
templateUrl: 'templates/ticketsPanel.html',
resolve: function(LoginService){return LoginService};
},
'productsPanel#': {
templateUrl: 'templates/productsPanel.html',
resolve: function(LoginService){return LoginService};
},
'categoriesPanel#': {
templateUrl: 'templates/categoriesPanel.html',
resolve: function(LoginService){return LoginService};
}
}
})
'productsPanel#' is like saying 'productsPanel' # nothing which mean the upper parent or root view.
https://github.com/angular-ui/ui-router/wiki/Multiple-Named-Views#view-names---relative-vs-absolute-names
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
})
My application has 3 'ui-views':
nav#
main#
side#
my states whitin the router are like this:
.state('home', {
url: "/home",
views: {
'nav#': { templateUrl: "views/navigation/main.html" },
'main#': { templateUrl: "views/home/main.html" },
'side#': { templateUrl: "views/home/side.html" }
}
})
this one works fine, all three views are rendered.
.state('home.detail', {
url: "/{id}",
views: {
'side#': { templateUrl: "views/home/side.html" }
}
})
This one works also, only the 'side#' view is updated.
Now for the third route, I want to keep the "nav#" view in the same state, like it is done in the "home.detail" route for the 'main#' and 'nav#' views.
.state('seminar', {
url: "/seminar/{id}",
views: {
'main#': { templateUrl: "views/seminars/main.html" },
'side#': { templateUrl: "views/seminars/side.html" }
}
})
Unfortunately, the 'nav#' view is rendered blank when opening the route. Is there a way to tell angular to not clear the view and keep the content?
I could declare the 'nav#' template, but I was wondering if there is a way around this.
Angular started off easy to understand but today I'm completely lost and would appreciated any help that is offered here for this problem I've come across.
So I have a two column page, one side the filters and the other the list of things to be filtered. This page is created using the bit of html injected in to the DOM. I have a filter template and a list template and they both have their own controllers (filterCtrl and filterCtrl).
I understood that I needed to seperate the http request for the controllers in to factories, so at the moment an example factory and controller in my app look like this.
fpApp.controller('listController', ['$scope', 'eventsFactory',
function($scope, eventsFactory) {
eventsFactory.eventList().then(
function(data){
$scope.events = data;
}
);
}
]);
and
fpApp.factory('eventsFactory', function ( $http ) {
return {
eventList: function() {
return $http.get('/js/db/events.json')
.then(
function(result) {
return result.data;
});
}
};
});
at the moment every thing is grand, I have a interface built up from partials and everything is outputting what I need. My problem is that I need to apply those filters to the list but have no idea how to get the information out of one controller in to the other controller or even one template to the other.. Once I'm able to get data from one to the other ill be good.
fpApp.config(function($stateProvider, $urlRouterProvider) {
//
// For any unmatched url, redirect to /state1
$urlRouterProvider.otherwise("/grid");
//
// Now set up the states
$stateProvider
.state('grid', {
url: '/grid',
views: {
// the main template will be placed here (relatively named)
'': { templateUrl: '/templates/grid.html' },
'filerList#grid': {
templateUrl: '/templates/partials/filterList.html',
controller: 'filterCtrl'
},
'viewSwitch#grid': {
templateUrl: '/templates/partials/viewSwitch.html'
},
'eventList#grid': {
templateUrl: '/templates/partials/eventList.html',
controller: 'listController'
}
}
})
.state('list', {
url: '/list',
views: {
// the main template will be placed here (relatively named)
'': { templateUrl: '/templates/list.html' },
'filerList#list': {
templateUrl: '/templates/partials/filterList.html',
controller: 'filterCtrl'
},
'viewSwitch#list': {
templateUrl: '/templates/partials/viewSwitch.html'
},
'eventList#list': {
templateUrl: '/templates/partials/eventList.html',
controller: 'listController'
}
}
});
});
So in summary how do I affect the list controller when the user is clicking filters in the filterController?
Thank you for your time in advance.
Here is my ui-router configuration:
$stateProvider
.state('search.filter.results', {
url: '/results?profileId&keywords',
views: {
'profiles#': {
templateUrl: 'profile-list/profile-list.html',
controller: 'ProfileListCtrl'
},
'profile-summary#search.filter.results': {
templateUrl: 'profile-list/profile-summary/profile-summary.html'
},
'profile-details#search.filter.results': {
templateUrl: 'profile-list/profile-details/profile-details.html',
controller: 'ProfileDetailsCtrl'
}
}
})
ProfileListCtrl has the following method:
$scope.showProfileDetails = function(profileId) {
$state.go('search.filter.results', { profileId: profileId });
};
The problem is, when this method is executed, ProfileListCtrl is instantiated and the view is reloaded.
I would like the reload to happen only if the keywords query parameter changes, but not when the profileId parameter changes.
What would be the best way to achieve this?
I would suggest: split this state into 2 states.
The "parent" keeping the profileId,
the "child" working with keywords.
Until the profileId is not changed, the parent controller won't be re-instantiated.
I created this example with this simplified states definition:
.state('profile', {
url: '/results/:profileId',
views: {
'profiles#': {
//templateUrl: 'profile-list/profile-list.html',
template: '<div>'
...
'<div ui-view=""></div></div>',
controller: 'ProfileCtrl'
},
}
})
.state('profile.results', {
url: '/?keywords',
views: {
'': {
//templateUrl: 'profile-list/profile-list.html',
template: ...
controller: 'ProfileListCtrl'
},
}
})
NOTE: state defintion here is simplified, to just show the split. Not all the details. Also, the child state target parents unnamed view ui-view="", because these should be nested. Finally, parent could even be abstract
This approach also will require some small adjustments of the url. It is not working well if the child url defintion would start with ? like: url: '?keywords'. So I used different pattern:
/results/profileId-value/?kewords=abc