Angularjs service doesn't work in run() method - angularjs

I got a simple get and set service in my angular app, that stores data from a $http request, but for some reason it just doesn't seem to work in angular's run() method. I'm not sure what am doing wrong.
my service
app.factory('sessionService', function() {
var user_info = {};
return {
set: function(value) {
user_info = value;
},
get: function() {
return user_info;
}
};
});
my run method
app.run(['$rootScope', '$location', 'Auth', 'sessionService',
function($rootScope, $location, Auth, sessionService) {
var routespermission = ['/dashboard', '/create']; //route that require login
$rootScope.$on('$routeChangeStart', function() {
if (routespermission.indexOf($location.path()) != -1) {
Auth.check({
type: 'checkSession'
}).success(function(data) {
if (data.status === false) {
$location.path('/user/login');
} else {
sessionService.set(data);
}
});
}
});
}
]);
trying to access the data in my controller
app.controller('dashboardCtrl', ['$scope', '$location', 'sessionService',
function($scope, $location, sessionService) {
$scope.user_info = sessionService.get();
console.log($scope.user_info);
$scope.create_review = function() {
}
}
]);
when I console log the service in my controller, it return an empty object. I don't getany error, so not sure where i went wrong

you should inject $route dependency in app.run for '$routeChangeStart' to work.
app.run(['$rootScope', '$location','$route', 'Auth', 'sessionService',
function($rootScope, $location,$route, Auth, sessionService) {
var routespermission = ['/dashboard', '/create']; //route that require login
$rootScope.$on('$routeChangeStart', function() {
if (routespermission.indexOf($location.path()) != -1) {
Auth.check({
type: 'checkSession'
}).success(function(data) {
if (data.status === false) {
$location.path('/user/login');
} else {
sessionService.set(data);
}
});
}
});
}
]);

Related

angularjs services with a returning test

I'm trying to create a serice in order to check if the user is connected before doing anything.
the service is:
app.service('generalService', function ($rootScope, ngDialog, Data, $location) {
this.checklogin = function(id) {
Data.get('checklogin').then(function (data) {
// no logged user, redirect to /login
if (!data) {
$location.path("/login");
}
return data;
});
}
});
And that's how I try to use it in a controller:
app.controller('childrenCtrl', function ($scope, $modal, $filter, Data, $location, $routeParams, generalService, childstatus) {
$scope.changepresence = function(child) {
generalService.checklogin($scope.child).then(function(res) {
if(data) {
// effective code here
}
});
}
});
I got the following error: "Cannot read property 'then' of undefined"
As I said in my comment. The method checklogin is already returning the data and not a promise. So you need to change it for this if you want to keep using the promise in the other method.
app.service('generalService', function ($rootScope, ngDialog, Data, $location) {
this.checklogin = function(id) {
return Data.get('checklogin');
}
});
app.controller('childrenCtrl', function ($scope, $modal, $filter, Data, $location, $routeParams, generalService, childstatus) {
$scope.changepresence = function(child) {
generalService.checklogin($scope.child).then(function(data) {
if(!data) {
// effective code here
$location.path("/login");
}
//process the data.
});
}

Promise returning object in angular js

i have my authservice as given below ,
myApp.factory('Authentication',
['$rootScope', '$location', 'URL', '$http', '$q',
function ($rootScope, $location, URL, $http, $q) {
var myObject = {
authwithpwd: function (user) {
var dfd = $q.defer();
$http
.post('Mart2/users/login', {email: user.email, password: user.password})
.then(function (res) {
return dfd.resolve(res.data);
}, function (err) {
return dfd.reject(err.data);
});
return dfd.promise;
} //login
};
return myObject;
}]); //factory
And i'm using that service in user service as follows :
myApp.factory('UserService',
['$rootScope', '$location', 'URL', '$http', '$q', 'Authentication',
function ($rootScope, $location, URL, $http, $q, $Authentication) {
var myObject = {
login: function (user) {
$Authentication.authwithpwd(user).then(function (regUser) {
console.log(regUser);
}).catch(function (error) {
$rootScope.message = error.message;
});
},
getUserToken: function () {
return $rootScope.currentUser.apiKey;
},
isLogged: function () {
if ($rootScope.currentUser) {
return true;
} else {
return false;
}
}
//login
};
return myObject;
}]); //factory
I am very new to angular js . While writing service and calling that service from controller i have put a console debug in user service which is showing its returning object .i am getting object if i do console.log(regUser) ? any idea why ?
To get the object you need to do change your myObject declaration. Basically you need to return a promise from the login function and then write a callback to get the resolved data.
myApp.factory('UserService',
['$rootScope', '$location', 'URL','$http','$q','Authentication',
function($rootScope,$location, URL,$http,$q,$Authentication) {
var myObject = {
login: function(user) {
var defer = $q.defer();
$Authentication.authwithpwd(user).then(function(regUser) {
console.log(regUser);
defer.resolve(regUser);
}).catch(function(error) {
$rootScope.message = error.message;
defer.reject(regUser);
});
return defer.promise;
},
getUserToken:function() {
return $rootScope.currentUser.apiKey;
},
isLogged:function() {
if($rootScope.currentUser){
return true;
} else {
return false;
}
}//login
};
return myObject;
}]); //factory
To extract the object from controller or from some other service you need to write a callback
UserService.login(user)
.then(function (data) {
$scope.data = data;
}, function (error) {
$scope.error = error;
});
Also in the Authentication service you can just do a 'dfd.resolve' instead of 'return dfd.resolve'; since you are already returning the dfd.promise.
I have created a fiddler here

angular controller function once $q.defer() is complete

I am new to angular and probably doing this completely wrong. On the /sites controller page, I want to access verifyAuth.getUserSiteAccess() to return a list of sites and build html links for the view.
I am using a google auth module, if the user logs in the userSites var is empty so I ping google, then call /api/index.php/login to return a list of user sites, then in this case finish with $q.defer().resolve(true);. Problem is the site controller function is trying to access userSites before it is defined. Is there a way to call $scope.test() after $q.defer().resolve is finished? or is there a better way to do this?
If I run setTimeout($scope.test, 500) it works fine.
Route -> Verify user access token, load userSites if undefined -> verify section access -> complete defer.
Site controller
'use strict';
angular.module('mps.sites', ['ngRoute'])
.controller('sites', ['verifyAuth', '$rootScope', '$scope', '$q', function(verifyAuth, $rootScope, $scope, $q) {
$scope.test = function() {
var test = verifyAuth.getUserSiteAccess();
console.log('test', test, '/test');
};
$scope.test();
}]);
** App.js routing and auth ** - not entire file...
'use strict';
angular.module('mps', [
'ngRoute',
'angularOauth',
'googleOauth',
'mps.global',
'mps.home',
'mps.sites',
'mps.site'
]).
config(['$routeProvider', function($routeProvider) {
$routeProvider.when('/', {
templateUrl: 'views/home/index.html',
controller: 'home',
resolve: {
auth: function(verifyAuth) {
verifyAuth.verifyUserAccess(true);
}
}
});
$routeProvider.when('/sites', {
templateUrl: 'views/sites/index.html',
controller: 'sites',
resolve: {
auth: function(verifyAuth) {
console.log('sites route selected');
verifyAuth.verifyUserAccess(false);
}
}
});
...
.factory('verifyAuth', ['$rootScope', '$window', '$q', '$location', '$timeout', '$http', 'Token',
function($rootScope, $window, $q, $location, $timeout, $http, Token) {
var userSites = null;
return {
deferLocation: function(isToken, index) {
var deferred = $q.defer();
var _location = $location;
if(isToken) {
switch(index) {
case true:
// Homepage/site toggling.
deferred.reject();
_location.path('/sites');
_location.replace();
break;
default:
// All pages.
deferred.resolve(true);
break;
}
} else {
// No token, redirect to login screen.
this.userError();
}
},
verifySectionAccess: function(userSites, siteName, index) {
if(siteName) {
// Subpage, verify section.
for(var i in userSites.sites) {
if(userSites.sites[i].sitename === siteName) {
this.deferLocation(true, index);
return false;
}
}
} else {
// Sites page.
this.deferLocation(true, index);
return false;
}
// No access to section.
this.userError();
return false;
},
// Check user permission are set.
verifyUserAccess: function (index, siteName) {
var token = Token.get();
var _this = this;
if(token) {
if(userSites) {
// Verify user section access.
_this.verifySectionAccess(userSites, siteName, index);
} else {
// Use google token to get user email and load site permissions.
Token.verifyAsync(token).
then(function(data) {
$http({method: 'GET', async: false, url: '/api/index.php/login/' + data.email}).success(function(d) {
userSites = d;
// Verify user access to specific section.
_this.verifySectionAccess(userSites, siteName, index);
});
}, function(error) {
_this.userError();
return false;
}
);
}
} else {
this.deferLocation(false, index);
}
},
getUserSiteAccess: function() {
console.log(userSites);
return userSites;
}
You have a number of issues here, all seem to be stemming from a misunderstanding of how promises work:
1) Functions that make async operations and are .then-able need to return a promise.
In your case, your deferLocation creates a promise (although doesn't return it), but it does not even do anything async.
On the other hand, the only function that does do something async (verifyUserAccess) doesn't have promises at all.
2) If you want a resolve parameter to be resolved with async value (as it seems to be with auth), then the function needs to return a promise. Then $route will wait until the promise is resolved. In your case, you don't return anything.
I suggest you read more about promises. Build something small with mock $timeout calls and ask specific questions if you run into problems.
Here's a high-level idea of how to build this:
app.factory("verifyAuth", function($q, Token, AuthSvc, SitesSvc) {
return {
verifyUserAccess: function(site){
var deferred = $q.defer();
var token = Token.token;
if (!token){
deferred.reject("no-token");
} else {
AuthSvc.verifyToken(token)
.then(function(result){
if (result.isValid){
// returns a promise to get a list of userSites
return SitesSvc.getSites(result.user);
} else {
return deferred.reject("token-not-valid");
}
})
.then(function(userSites){
if (checkAccessPermissions(site, userSites)){
deferred.resolve(true);
} else {
deferred.resolve(false);
}
})
.catch(function(error){
// some other error
deferred.reject(error);
});
}
return deferred.promise;
}
};
});
Router calls return verifyUser.buildUserData in the resolve, this checks if there is a token, if not logs the user out. Then checks if there is a site list global variable, if not pings the token to google to get the user email and the user email to the database to get the site list, from there loop through the list and check auth if a site name is passed into buildUserData.
The below example will handle all the auth before the view is rendered. Thanks New Dev for pointing me in the right direction.
Router:
...
config(['$routeProvider', function($routeProvider) {
$routeProvider.when('/', {
templateUrl: 'views/home/index.html',
controller: 'home',
resolve: {
auth: function(verifyUser) {
return verifyUser.homepageRedirect();
}
}
});
$routeProvider.when('/sites', {
templateUrl: 'views/sites/index.html',
controller: 'sites',
resolve: {
auth: function(verifyUser) {
return verifyUser.buildUserData();
}
}
});
$routeProvider.when('/sites/:site/scheduling', {
templateUrl: 'views/sites/scheduling/index.html',
controller: 'site',
resolve: {
auth: function(verifyUser, $route) {
return verifyUser.buildUserData($route.current.params.site);
}
}
});
...
Factories
.factory('getUserEmail', ['$rootScope', '$window', '$q', '$location', '$timeout', '$http', 'Token', function($rootScope, $window, $q, $location, $timeout, $http, Token) {
return {
// Get user email address based on token.
getEmail: function() {
var deferred = $q.defer();
var token = Token.get();
$http({method: 'GET', async: false, url: 'https://www.googleapis.com/oauth2/v1/tokeninfo', params: {access_token: token }}).
success(function(data) {
$rootScope.username = data.email;
deferred.resolve(data);
return data;
}).error(function(data) {
deferred.reject(data);
});
return deferred.promise;
}
}
}])
.factory('getUserSites', ['$rootScope', '$window', '$q', '$location', '$timeout', '$http', 'Token', function($rootScope, $window, $q, $location, $timeout, $http, Token) {
return {
// Get user site access.
getSites: function() {
var deferred = $q.defer();
console.log('site list is undefined.');
$http({method: 'GET', async: false, url: '/api/index.php/login/' + $rootScope.username}).
success(function(data) {
$rootScope.sites = data.sites;
deferred.resolve(data);
}).error(function(data) {
deferred.reject(data);
});
return deferred.promise;
}
}
}])
.factory('verifyUser', ['$rootScope', '$window', '$q', '$location', '$timeout', '$http', 'Token', 'getUserEmail', 'getUserSites', function($rootScope, $window, $q, $location, $timeout, $http, Token, getUserEmail, getUserSites) {
return {
siteError: function() {
localStorage.removeItem('accessToken');
$location.path('/');
},
// Redirect user to /sites if logged in.
homepageRedirect: function() {
var deferred = $q.defer();
var token = Token.get();
if(!token) {
deferred.resolve(true);
} else {
deferred.reject(true);
$location.path('/sites');
}
return deferred.promise;
},
// Verify user token exists and they have access to the section.
buildUserData: function(site) {
console.log('site',site,'/site');
var deferred = $q.defer();
var token = Token.get();
var _this = this;
if(!token) {
deferred.reject('no token');
localStorage.removeItem('accessToken');
$location.path('/');
} else {
if($rootScope.sites) {
console.log('site list present, check access.');
if(site) {
var data = $rootScope.sites;
console.log(data, site);
for(var i in data) {
console.log(data[i].sitename);
if(data[i].sitename === site) {
console.log('user has access, render view.');
deferred.resolve(true);
return false;
}
}
console.log('No site access, logout.');
deferred.reject('No access to site.');
_this.siteError();
} else {
console.log('No access needed, landing page.');
deferred.resolve(true);
}
} else {
console.log('no site list, get user email from google and query db with user.');
getUserEmail.getEmail().then(function(data) {
return getUserSites.getSites();
}).then(function(data) {
if(site) {
console.log('sub page');
for(var i in data.sites) {
console.log(data.site[i]);
if(data.sites[i].sitename === site) {
console.log('user has access, render view.');
deferred.resolve(true);
return false;
}
}
console.log('No site access, logout.');
deferred.reject('No access to site.');
_this.siteError();
} else {
deferred.resolve(true);
}
}).catch(function(data) {
deferred.reject(true);
_this.siteError();
});
}
}
return deferred.promise;
}
}
}]);

Angularjs resource handling response & custom methods

Is it possible within the AuthService to handle the response and set the session with the SessionService?
I doing it in the controller right now with the success callback but I'm still new to Angularjs and trying to understand how to customize a resource.
I'm using Angularjs 1.1.5
app.factory('AuthService', ['$resource', 'SessionService',
function($resource, SessionService) {
return $resource(
'/api/v1/auth/:action',
{action:'#action'},
{
login: {
method:'POST',
params: {
action: 'login'
}
},
logout: {
method:'GET',
params: {
action: 'logout'
}
}
}
);
}
]);
app.controller('LoginCtrl', ['$scope', '$location', 'AuthService', 'SessionService' function LoginCtrl($scope, $location, AuthService, SessionService) {
$scope.credentials = { email: "", password: ""};
$scope.login = function() {
AuthService.login($scope.credentials).success(function() {
SessionService.set('authenticated', true);
$location.path('/home');
});
}
}]);
app.factory("SessionService", function() {
return {
get: function(key) {
return sessionStorage.getItem(key);
},
set: function(key, val) {
return sessionStorage.setItem(key, val);
},
unset: function(key) {
return sessionStorage.removeItem(key);
}
}
});
I'm not sure that $resource is the appropriate abstraction to use here. I think it would be much simpler to implement your AuthService using plain $http. Just implement login and logout as normal function, then you can feel free to do whatever you want there. You should also make sure you return the promise, that way whoever calls login() or logout() can still do .then() on it if they need to do additional things after login. Here's an example:
app.factory('AuthService', ['$http', '$location' 'SessionService',
function($http, $location, SessionService) {
var baseUrl = '/api/v1/auth/';
function onLoginSuccess(data){
SessionService.set('authenticated', true);
$location.path('/home');
}
function onLoginFailure(error){
SessionService.unset('authenticated');
$location.path('/login');
}
return {
login: function(credentials){
return $http.post(baseUrl+'login', credential).then(onLoginSuccess, onLoginFailure);
}
logout: function(){
return $http.get(baseUrl+'logout');
}
};
app.controller('LoginCtrl', ['$scope', 'AuthService', function LoginCtrl($scope, AuthService) {
$scope.credentials = { email: "", password: ""};
$scope.login = function() {
AuthService.login($scope.credentials);
}
}]);
app.factory("SessionService", function() {
return {
get: function(key) {
return sessionStorage.getItem(key);
},
set: function(key, val) {
return sessionStorage.setItem(key, val);
},
unset: function(key) {
return sessionStorage.removeItem(key);
}
}
});
Your server side script on path /api/v1/auth/login should return a result to indicate that the login is successfully granted or not.
For example, If the log in is granted, then /api/v1/auth/login returns the success response 200.
If the login is denied, then /api/v1/auth/login returns the bad request response (failure) 400.
If with this condition, your code should be written as followed,
app.controller('LoginCtrl', ['$scope', '$location', 'AuthService', 'SessionService'
function LoginCtrl($scope, $location, AuthService, SessionService) {
$scope.credentials = { email: "", password: ""};
$scope.login = function() {
AuthService.login($scope.credentials, function(data) { //Success callback
SessionService.set('authenticated', true);
$location.path('/home');
}, function(error){ //Failure callback
SessionService.unset('authenticated');
$location.path('/login');
});
}
}]);

AngularJS Service Passing Data Between Controllers

When using an AngularJS service to try and pass data between two controllers, my second controller always receives undefined when trying to access data from the service. I am guessing this is because the first service does a $window.location.href and I'm thinking this is clearing out the data in the service? Is there a way for me to change the URL to a new location and keep the data persisted in the service for the second controller? When I run the code below the alert in the second controller is always undefined.
app.js (Where Service is Defined)
var app = angular.module('SetTrackerApp', ['$strap.directives', 'ngCookies']);
app.config(function ($routeProvider)
{
$routeProvider
.when('/app', {templateUrl: 'partials/addset.html', controller:'SetController'})
.when('/profile', {templateUrl: 'partials/profile.html', controller:'ProfileController'})
.otherwise({templateUrl: '/partials/addset.html', controller:'SetController'});
});
app.factory('userService', function() {
var userData = [
{yearSetCount: 0}
];
return {
user:function() {
return userData;
},
setEmail: function(email) {
userData.email = email;
},
getEmail: function() {
return userData.email;
},
setSetCount: function(setCount) {
userData.yearSetCount = setCount;
},
getSetCount: function() {
return userData.yearSetCount;
}
};
});
logincontroller.js: (Controller 1 which sets value in service)
app.controller('LoginController', function ($scope, $http, $window, userService) {
$scope.login = function() {
$http({
method : 'POST',
url : '/login',
data : $scope.user
}).success(function (data) {
userService.setEmail("foobar");
$window.location.href = '/app'
}).error(function(data) {
$scope.login.error = true;
$scope.error = data;
});
}
});
appcontroller.js (Second controller trying to read value from service)
app.controller('AppController', function($scope, $http, userService) {
$scope.init = function() {
alert("In init userId: " userService.getEmail());
}
});
Define your service like this
app.service('userService', function() {
this.userData = {yearSetCount: 0};
this.user = function() {
return this.userData;
};
this.setEmail = function(email) {
this.userData.email = email;
};
this.getEmail = function() {
return this.userData.email;
};
this.setSetCount = function(setCount) {
this.userData.yearSetCount = setCount;
};
this.getSetCount = function() {
return this.userData.yearSetCount;
};
});
Check out Duncan's answer here:
AngularJS - what are the major differences in the different ways to declare a service in angular?

Resources