angularJS $stateProvider - resolve $scope - angularjs

I'm trying to resolve $scope for component-usage reasons but it seems to be a problem... please look at the code and let me know if you see a problem or have a different idea of how to implement it.
angular.module('app').config(
function ($stateProvider) {
$stateProvider.state('home', {
url: '/home',
views: {
'': {
template: `<my-comp user-logged-in="userModel"></my-comp>`
}
},
resolve: {
'$scope': function ($rootScope) {
var scope = $rootScope.$new();
scope.userModel = {userId:1, name:'user'};
return scope;
}
}
})
});
As you can see I'm trying to pass the userModel's data directly to the component controller, but it fails...
Currently I'm using the following logic which work's fine but i'm trying something new :)
views: {
'': {
template: `<my-comp user-logged-in="$ctrl.userModel"></my-comp>`,
controller: function (userLoggedIn) {
this.userModel = userLoggedIn;
},
controllerAs: '$ctrl'
}
},
resolve: {
userLoggedIn: function (userLoggedIn) {
return userLoggedIn;
}
}
Please advise.

Related

Using resolve functions once in angular js and angular router to few controllers

Ui router looks like:
<div ui-view="test1">
<div ui-view="test2">
My routing:
.state('testsPage', {
url: "/",
// templateUrl: "public/src/settings/settings.php",
views: {
'test1': {
templateUrl: 'public/src/test2.html',
controller: 'test2Controller',
controllerAs: 'vm',
resolve: {
getData: function(testService) {
return testService.getdata();
}
}
},
'test2': {
templateUrl: 'public/src/test1.html',
controller: 'test1ontroller',
controllerAs: 'vm',
resolve: {
getData: function(testService) {
return testService.getdata();
}
}
},
I want to call getData only once and not inject it the each controller
If I do that in each controller it means that I call to getData from server two times in loading.
I tried to use controller inheritance but it ask to inject that service
// Inherit from Base Controller
angular.extend(vm, $controller('baseController', {
getData: getData,
testService: testService,
$rootScope: $rootScope
}));
How can I make it like inheritance?
Do I need to move it to run the function? If yes do I have to use rootScope to get that data in the controller?
Try putting the resolve on the parent:
.state('testsPage', {
url: '/',
views: ...,
resolve: {
getData: function(testService) {
return testService.getdata();
}
}
...
}
You would then need to add getData as a dependancy on each views controller:
// Controller
function test1ontroller(getData) {
}

Share resolve in Angular UI Router

I have various states that use the same resolve to load a timeLog into my controller $scope before the controller loads. I would like to not reproduce this code but share it between these views. I'm fairly new to JS frameworks, and especially Angular JS.
I'm having a hard time googling this and not finding any decent information. Maybe I'm searching incorrectly or not thinking about this correctly. Any Suggestions?
.config(function($stateProvider) {
$stateProvider
.state('tab.edit-log-summary', {
url: '/logs/edit-log-summary/:timeLogId',
views: {
'tab-logs': {
templateUrl: 'templates/logs/edit-log-summary.html',
controller: 'EditLogSummaryCtrl'
}
},
resolve: {
timeLog: function(config, $stateParams, DailyLog) {
return DailyLog.get({id: $stateParams.timeLogId});
},
}
})
.state('tab.edit-time-log', {
url: '/logs/edit-time-log/:timeLogId',
views: {
'tab-logs': {
templateUrl: 'templates/logs/edit-time-log.html',
controller: 'EditTimeLogCtrl'
}
},
resolve: {
timeLog: function(config, $stateParams, DailyLog) {
return DailyLog.get({id: $stateParams.timeLogId});
},
}
})
})
This really goes down to vanilla Javascript. The resolves are objects. Just define them as a single object somewhere above and pass it to the resolve property each time.
.config(['$stateProvider',function($stateProvider) {
var timeLogResolve = {
timeLog: ['config','$stateParams','DailyLog',function(config, $stateParams, DailyLog) {
return DailyLog.get({id: $stateParams.timeLogId});
}]
};
$stateProvider
.state('tab.edit-log-summary', {
url: '/logs/edit-log-summary/:timeLogId',
views: {
'tab-logs': {
templateUrl: 'templates/logs/edit-log-summary.html',
controller: 'EditLogSummaryCtrl'
}
},
resolve: timeLogResolve,
}
})
.state('tab.edit-time-log', {
url: '/logs/edit-time-log/:timeLogId',
views: {
'tab-logs': {
templateUrl: 'templates/logs/edit-time-log.html',
controller: 'EditTimeLogCtrl'
}
},
resolve: timeLogResolve,
}
})
}])
One suggestion- use inline array notation for providing dependencies. This helps protect your code from breaking if you minify it. I did that myself in the demo above, but I leave it to your discretion to keep it.

Resolve not called the second time

I have some routes defined like this :
$stateProvider
.state('app', {
url: '/',
abstract: true,
views: {
'menuContent': {
templateUrl: 'templates/home.html'
}
}
})
.state('app.restricted', {
url: '/restricted',
views: {
'content': {
templateUrl: 'templates/restricted/restricted-dashboard.html',
controller: 'RestrictedController as vmRestricted'
}
},
resolve: {
isGranted: 'isGranted'
}
})
.state('app.restricted.pending', {
url: '/pending',
views: {
'tabsView': {
templateUrl: 'templates/restricted/restricted-manage-pending.html',
controller: 'RestrictedPendingController as vm'
}
},
resolve: {
isGranted: 'isGranted'
}
})
.state('app.restricted.devices', {
url: '/devices',
views: {
'tabsView': {
templateUrl: 'templates/trusted/restricted-manage-devices.html',
controller: 'RestrictedDevicesController as vm'
}
},
resolve: {
isGranted: 'isGranted'
}
})
.state('app.grant', {
url: '/grant-access',
views: {
'content': {
templateUrl: 'templates/grant-access.html',
controller: 'GrantAccessController as vm'
}
}
})
;
In these routes I have a restricted area and a grant access page to grant access to the restricted area.
When the isGranted resolve provider is rejected I redirect to the app.grant route.
This is the code doing this :
$rootScope.$on(AngularEvents.STATE_CHANGE_ERROR, _onStateChangeError);
function _onStateChangeError(event, toState, toParams, fromState, fromParams, error){
switch (error) {
case 'accessRejected':
$state.go('app.grant');
break;
}
}
Here is the code of my isGranted provider :
(function() {
'use strict';
angular.module('app')
.provider('isGranted', isGrantedProvider);
isGrantedProvider.$inject = [];
function isGrantedProvider() {
this.$get = isGranted;
isGranted.$inject = ['$q', '$log', 'grantService'];
function isGranted($q, $log, grantService){
$log.log('isGrantedProvider');
if (grantService.isGranted()) {
return $q.when(true);
} else {
return $q.reject('accessRejected');
}
}
}
})();
(grantService.isGranted() just returns a boolean value)
The first time I go to the app.restricted route with $state.go('app.restricted') the provider is executed.
The route is rejected because the access is not granted and we are redirected to the app.grant route.
In this page, the user can log in and have access to the restricted area. Once the user is logged in we redirect him to the app.restricted.pending route but the resolve is not called and the route is rejected and we are redirected to the app.grant route again, whereas the access was granted.
Why is the resolve not called?
Is there a way to force it?
EDIT
I have new information after some testing.
I saw that the resolve is not called the second time only when it is a service:
This resolve is always executed when we enter the state:
state('app.restricted', {
url: '/restricted',
views: {
'content': {
templateUrl: 'templates/restricted/restricted-dashboard.html',
controller: 'RestrictedController as vmRestricted'
}
},
resolve: {
isGranted: ['$log', function($log) {
$log.log('RESOLVE');
}]
}
})
But this resolve is only executed once even when I enter again to the state:
state('app.restricted', {
url: '/restricted',
views: {
'content': {
templateUrl: 'templates/restricted/restricted-dashboard.html',
controller: 'RestrictedController as vmRestricted'
}
},
resolve: {
isGranted: 'isGranted'
}
})
angular.module('app')
.provider('isGranted', isGrantedP);
isGrantedP.$inject = [];
function isGrantedP() {
this.$get = isGranted;
isGranted.$inject = ['$q', '$log'];
function isGranted($q, $log){
$log.log('RESOLVE');
}
}
Why isn't this service called each time? Is it because a service is a singleton? How should I proceed?
After a lot of investigations and testing I found the solution!
First, let's see why it is not working
As mentioned in the docs (http://angular-ui.github.io/ui-router/site/#/api/ui.router.state.$stateProvider), if the resolve is a string, then it corresponds to a service
factory - {string|function}: If string then it is alias for service.
Otherwise if function, it is injected and return value it treated as
dependency. If result is a promise, it is resolved before its value is
injected into controller.
And as mentioned in the angularjs docs (https://docs.angularjs.org/guide/providers), all services are singletons, meaning that it will be instantiated only once
Note: All services in Angular are singletons. That means that the
injector uses each recipe at most once to create the object. The
injector then caches the reference for all future needs.
Why is it important?
Because resolves do not call a function inside our service. They just use the return value of the instantiated service. BUT because our service will be instantiated only once, the return value will always be the same! (because our service initialization is only called once)
What can we do?
From my tests I could see that a resolve defined like this:
resolve: {
myResolve: ['$log', function($log) {
$log.log('My Resolve!');
}]
}
is always executed, so we can write them this way to make it work correctly.
But how can I do if I want to use my service?
The best working solution I found to be able to use my service and have a syntax that looks similar to this one: myResolve: 'myResolveService' is to declare my resolve like this:
resolve: {
myResolve: ['myResolveService', function(MyResolveService) {
myResolveService.log();
}]
}
And my service like this:
angular.module('app')
.factory('myResolve', myResolve);
myResolve.$inject = ['$log'];
function myResolve($log) {
function service(){
this.log = log;
function log() {
$log.log('My resolve!');
}
}
return new service();
}
This code can also be adapted for resolves that return a promise:
Resolve:
resolve: {
myResolve: ['myResolveService', function(MyResolveService) {
return myResolveService.check();
}]
}
Service:
angular.module('app')
.factory('myResolve', myResolve);
myResolve.$inject = ['$q', 'myService'];
function myResolve($q, myService) {
function service(){
this.check = check;
function check() {
var defer = $q.defer();
if (myService.check()) {
defer.resolve(true);
} else {
defer.reject('rejected');
}
return defer.promise;
}
}
return new service();
}

UI-route nested views and resolve not working

I have a problem with resolve and my nested view is not loading because ot that. I don't see where is the problem. This was working with ng-route.
Here is my situation. We should see "View3" text bellow other tow. If you remove the resolve it will work.
[http://plnkr.co/edit/p483wWVSp30LGiQMuk1r?p=preview][1]
[1]: http://plnkr.co/edit/p483wWVSp30LGiQMuk1r?p=preview
Here is my State with Resolve. The problem is that i used $route as parameter. $route is averrable for ng-route module.
.state('test.subs.quest', {
url: '/:bob',
views: {
'quest#test': {
template: "View3",
controller: function($stateParams) {
console.log($stateParams);
}
},
},
resolve: {
Questions: function(Questionnaire, $route) {
console.log('any body here ?');
return Questionnaire.Get($route.current.params.slug);
}
}
});
and it should be like this:
.state('categories.category.questionnaire', {
url: '/:slug',
views:{
"main#":{
template: '<div compilehtml="html"></div>',
controller: 'QuestionnaireController',
}
},
resolve: {
Questions: function(Questionnaire, $stateParams ) {
return Questionnaire.Get($stateParams.slug);
}
}
})

Angular ui-router resolve not working

I am trying to load a get service JSON function in the main state resolve function so I can store the data to a scope variable.
The account JSON information is relevant because all sub pages are essentially dependent on the information.
--
The below code is partially working. The account resolve function is being successfully called and even the $http returns a promise (state === 0 though). The issue is when the account function resolves the state.controller is never being called.
$stateProvider
.state('app',{
url: '/',
views: {
'header': {
templateUrl: '../views/templates/partials/header.html',
},
'content': {
templateUrl: '../views/templates/partials/content.html'
},
'footer': {
templateUrl: '../views/templates/partials/footer.html',
}
},
resolve: {
account: function($timeout, accountFactory){
//Comment
return $http({method: 'GET', url: '/account.json'});
}
},
controller: ['$scope', 'account', function($scope, account){
// You can be sure that promiseObj is ready to use!
$scope.data = account;
console.log('SCOPE!!!!!');
}],
})
.state('app.accessory', {
url: 'accessory',
views: {
'content#': {
templateUrl: '../views/accessory/listing.html',
controller: 'accessoryListingCtrl',
controllerAs: 'vm'
}
}
})
}]);
Your parent state config is not correct. When using multiple named views A controller does not belong to a state but to a view, so you should move your controller statement to the specific view declaration, or all of them if you need it everywhere.
See here: https://github.com/angular-ui/ui-router/wiki/Multiple-Named-Views
$stateProvider
.state('report',{
views: {
'filters': {
templateUrl: 'report-filters.html',
controller: function($scope){ ... controller stuff just for filters view ... }
},
'tabledata': {
templateUrl: 'report-table.html',
controller: function($scope){ ... controller stuff just for tabledata view ... }
},
'graph': {
templateUrl: 'report-graph.html',
controller: function($scope){ ... controller stuff just for graph view ... }
},
}
})
I don't know why the controller does not get called. But you can start by making sure that resolve always return data.
resolve: {
account: function($timeout, accountFactory){
//Comment
return $http({method: 'GET', url: '/account.json'})
.$promise.then(
function(data) { return data; },
function(error) { return error; });
}
}

Resources