Angular UI + UI Router: How to inherit Resolved states - angularjs

I have a parent state with 3 child states. The parent state retrieves data using promises and returns resolved data in an object (named 'data') so it is accessible to controller and views. Some child states need the resolved parent data also to define their own object named data. But it seems that when transitioning from sibling states their resolves are never reached (a breakpoint in the corresponding state definition never gets hit).
In short: when transferring from state 'settings.account.person' to sibling state 'settings.account.password' I want the resolve statement to be executed. Currently it is not being hit at all...
States:
.state('settings.account', {
url: '/account'
,resolve: {
menu: ['menu','MenuService', function (menu,MenuService) {
return MenuService.retrieveSubMenuByParentUrl(menu,'/settings/account');
}],
account: ['UserManagementService',function(UserManagementService) {
return UserManagementService.account();
}],
data: ['menu',function (menu) {
return {menu:menu};
}]
}
,views: {
'setting#settings': {
templateUrl: '/app/components/settings/account/views/tabpanel.html'
,controller: 'AccountController'
}
}
})
.state('settings.account.person', {
url: '/person',
resolve: {
languages: ['APIService',function(APIService) {
return APIService.call(AppConfig.API_ENDPOINTS.language);
}],
data: ['menu','account','languages',function(menu,account,languages){
return {
menu: menu,
account: account,
languages: languages
};
}]
}
,views: {
'tabContent#settings.account': {
templateUrl: '/app/components/settings/account/views/person.html'
,controller: 'AccountController'
}
}
})
.state('settings.account.password', {
url: '/password'
,data: ['account',function(account){
return {
account: account
};
}]
,views: {
'tabContent#settings.account': {
templateUrl: '/app/components/settings/account/views/password.html'
,controller: 'AccountController'
}
}
})
.state('settings.account.delete', {
url: '/delete'
,data: ['account',function(account){
return {
id: account.id
};
}]
,views: {
'tabContent#settings.account': {
templateUrl: '/app/components/settings/account/views/deleteAccount.html'
,controller: 'AccountController'
}
}
})

Oops.. I see that in states 'settings.account.password' and 'settings.account.delete' I forgot half of the resolve block.... No wonder it doesn't get hit.

Related

How to reset $stateparam parameter value to null in angularjs?

This is my code.
.state('appSetting.studentList', { url: '/student', views: { 'list#appSetting': { templateUrl: appUrl + '/Student/List', controller: 'StudentCtrl' } } })
.state('appSetting.studentList.details', { url: '/:studentId', views: { 'details#appSetting': { templateUrl: appUrl + '/Student/Edit', controller: 'StudentEditCtrl' } } })
.state('appSetting.employeeList', { url: '/employee', views: { 'list#appSetting': { templateUrl: appUrl + '/Employee/List', controller: 'EmployeeCtrl' } } })
.state('appSetting.employeeList.details', { url: '/:employeeId', views: { 'details#appSetting': { templateUrl: appUrl + '/Employee/Edit', controller: 'EmployeeEditCtrl' } } })
I have a add button on layout , when user click the add button call the following function.
$scope.gotoAdd = function () {
if ($state.current.name.indexOf(".details") == -1) {
$state.go($state.current.name + ".details")
}
else {
var paramId = $state.current.url.slice(2);
$state.go($state.current.name, ({ studentId: "", reload: true }));
}
};
In the above code working fine, but when open employee page user click add button i want to empty of employeeId parameter. I have number of links so if condition is using lengthy process. The above variable paramId dynamically changed (one time "studentId" , "employeeId" ,..............) depends on current state.
I tried the following code but not working because paramId contains string value
$state.go($state.current.name, ({ paramId: "", reload: true }));
Why are you putting the second parameter in '( )' in $state.go().
Try
$state.go($state.current.next, {}, {reload: true});
or
$state.go($state.current.next, {paramId: ""}, {reload: true});
$scope.gotoAdd = function () {
if ($state.current.name.indexOf(".details") == -1) {
$state.go($state.current.name + ".details")
}
else {
for (var prop in $stateParams) {
if ($stateParams.hasOwnProperty(prop)) {
$stateParams[prop] = '';
}
}
$state.go($state.current.name,$stateParams, { reload: true });
}
};
$state.go can take an inherit boolean parameter that serve exactly for this purpose.
As of UI router 0.2.15 -probably an old version, the documentation states
inherit - {boolean=true}, If true will inherit url parameters from current url.
In fact, it reset not only url parameters, but all $stateParams to their default, like what they are in state definition.
This will keep only state params as passed through the params object and all other are set to default or deleted.
$state.go('target.state', params, {inherit: false});

How to pass data from $state.go() to the data parameter in stateprovider in angular?

I am having trouble passing some data to the stateprovider using $state.go(). Here is the sample code that we have been using.
$stateProvider.state('socialform', {
url: "/socialform?webcontent",
templateUrl: "base_template/_Sends.html?",
data: { pageTitle: 'Social & Website Publishing' },
resolve: {
callPreRenderServices: callPreRenderServices
}
});
$scope.isWebContent = function(status) {
if(status) {
$state.go('socialform', {webcontent:true});
}
else {
$state.go('socialform');
}
};
Basically, what we need to be doing is to pass a title variable to $state.go() so that it will replace the pageTitle to whatever is the value of the passed variable.
From the code above to this:
$stateProvider.state('socialform', {
url: "/socialform?webcontent",
templateUrl: "base_template/_Sends.html?",
data: { pageTitle: title },
resolve: {
callPreRenderServices: callPreRenderServices
}
});
$scope.isWebContent = function(status) {
if(status) {
$state.go('socialform', {webcontent:true, title:"some title"});
}
else {
$state.go('socialform', {title:"another title"});
}
};
You could use a service :
module.service('titleService', function() {
this.title = null;
});
// ... inject titleService in the calling controller ...
$scope.isWebContent = function(status) {
if(status) {
titleService.title = 'Some Title'
$state.go('socialform');
}
else {
titleService.title = 'Another Title'
$state.go('socialform');
}
};
Then, you can either inject it in via custom data or, via the resolve function :
// ... inject before route definition, via dependency injection
data = { title: titleService.title };
$stateProvider.state('socialform', {
url: "/socialform?webcontent",
templateUrl: "base_template/_Sends.html?",
// like this
data: data,
resolve: {
callPreRenderServices: callPreRenderServices
// Or you can resolve your title from your service
// and use pageTitle in your controller
pageTitle: ['titleService', function(titleService) {
return titleService.title;
}]
}
});
You could also pass it as a $state parameter :
$stateProvider.state('socialform', {
url: "/socialform/:webcontent/:title",
// ...
});
// ...
$state.go('socialform', {webcontent: 'something', title: 'some other thing'});

Can I group multiple objects in UI router resolve

I need to load 3 services up front in Resolve , rather than creating seperate service calls for each, will it be possible to return a object of functions
My router
.state('home.movies', {
parent: 'home',
url: '/home/movies/edit/:id',
data: {
roles: []
},
views: {
'content#': {
templateUrl: 'views/admin/activity-edit.html',
controller: 'ActivityEditController'
}
},
resolve:
titles:function (MovieService){
return {
actors: function (MovieService) {
return MovieService.getActors('','','N');
},
actresses:function (MovieService) {
return ResourceService.getActresses();
},
timemap:function(MovieService){
return MovieService.getTimemap();
}
}
}
});
})
my Controller
.controller('MovieEditController', function ($scope,titles,MovieService) {
refere is as
$scope.moviesActors=titles.actors;
It's possible with $q:
resolve: {
titles: function (MovieService, $q){
return $q.all({
actors: MovieService.getActors('','','N'),
actresses: ResourceService.getActresses(),
timemap: MovieService.getTimemap()
});
}
}
Resolved titles will look like: { actors: [], actresses: [], timemap: {} }

how to add Custom Data to each State Objects Dynamically

In the below example i have custom data like data:{roles:[]} like this i need to add custom data properties dynamically like data:{user:[]} for each state
.state('WorkArea', {
parent: 'site',
url: '/WorkArea',
data: {
roles: ['User', 'Dev']
},
})
Define it in this way:
.state('WorkArea', {
parent: 'site',
url: '/WorkArea',
data: {
roles: (function() {
return ['User', 'Dev'];
})()
},
})
You can use IIFE for this case to dynamically add properties to data attribute.
In your app.run use:
$rootScope.$on('$stateChangeStart', function(event, toState){
var roles = toState.data.roles ;
console.log(roles);
// your custom logic here for that state
})
Copied from: https://github.com/angular-ui/ui-router/wiki
You can attach custom data to the state object (we recommend using a data property to avoid conflicts).
// Example shows an object-based state and a string-based state
var contacts = {
name: 'contacts',
templateUrl: 'contacts.html',
data: {
customData1: 5,
customData2: "blue"
}
}
$stateProvider
.state(contacts)
.state('contacts.list', {
templateUrl: 'contacts.list.html',
data: {
customData1: 44,
customData2: "red"
}
})
With the above example states you could access the data like this:
function Ctrl($state){
console.log($state.current.data.customData1) // outputs 5;
console.log($state.current.data.customData2) // outputs "blue";
}

UI-Router's resolve functions are only called once- even with reload option

In Angular ui router, when $state.go("main.loadbalancer.readonly"); is ran after main.loadbalancer.readonly has been previously activated, my resolve: {} is not being evaulauted/executed. The resolve is simply bypassed..I have verified this with the console.log($state.current.data['deviceId']); not showing.
angular.module("main.loadbalancer", ["ui.bootstrap", "ui.router"]).config(function($stateProvider) {
return $stateProvider.state("main.loadbalancer", {
url: "device/:id",
views: {
"content#": {
templateUrl: "loadbalancer/loadbalancer.html",
controller: "LoadBalancerCtrl"
}
}
}).state("main.loadbalancer.vips", {
resolve: {
isDeviceReadOnly: function($state) {
console.log($state.current.data['deviceId']);
if (!$state.current.data['deviceId']) {
console.log("pimp");
$state.go("main.loadbalancer.readonly");
}
}
},
url: "/vips",
templateUrl: "loadbalancer/vip-table.html",
controller: "VipListCtrl"
}).state("main.loadbalancer.readonly", {
url: "/readonly",
templateUrl: "loadbalancer/readonly.html",
controller: "ReadonlyCtrl"
});
});
Controller code:
submit = function() {
$state.current.data = { deviceId: false };
return LoadBalancerSvc.searchDevice($scope.searchInput.value).get().then(function(lb) {
console.log(lb.ha_status);
if (lb.ha_status == "secondary") {
console.log("hi");
$state.current.data['deviceId'] = false;
$state.go("main.loadbalancer.readonly"); //WHEN THIS IS RAN A SECOND TIME
AFTER STATE HAS BEEN ACTIVE BEFORE
$state.deviceReadonly = true
} else {
$state.current.data['deviceId'] = lb.id;
$state.deviceReadonly = false;
SearchSvc.updateDeviceNumber(lb.id);
$state.go("main.loadbalancer.vips", {id: lb.id});
console.log("bye");
}
});
};
I can only guess that since main.loadbalancer.vips has been activated previously, then to ui router it means once resolved always resolved. How can I make it to where each time the state is activated with $state.go("main.loadbalancer.readonly") resolve will be evaluated?
Note: I have also tried $state.go("main.loadbalancer.readonly", { reload: true }); to no success.
It seems that 'transitionTo' works instead.
if (lb.ha_status == "secondary") {
$state.current.data['deviceId'] = false;
$state.transitionTo("main.loadbalancer.readonly", {}, { reload: true });
$state.deviceReadonly = true
}

Resources