Related
I am using Facebook SDK to build an application however I am faced with a challenge, I try to get the login status of the user before I redirect him to his profile page, however during the call to get the login status I get the error that
ReferenceError: FB is not defined
now the SDK is being loaded asynchronously so the error makes sense, how can i resolve the problem. Here is my code:
app.config(function ($routeProvider) {
$routeProvider
.when('/', {
templateUrl: "Views/ListPages.html",
controller: 'MainCtrl',
resolve: {
authentication:["$location", "LoginFactory", function($location, LoginFactory){
console.log("in resolve");
LoginFactory.getLoginStatus()
.then(function(response){
if(response){
$location.path('/login');
}
else{
$location.path('/');
}
});
}]
}
})
.when('/login', {
templateUrl: "Views/Login.html",
controller: 'LoginCtrl'
})
.otherwise
({redirectTo: '/'});
});
loginApp.factory("LoginFactory", function ($rootScope, $q, $location, UserInfo) {
return {
getLoginStatus: function () {
var deferred = $q.defer();
FB.getLoginStatus(function (response) {
if(!response || response.error){
deferred.reject(new error("User Not logged in."));
}
else{
deferred.resolve(response);
}
});
return deferred.promise;
},
login: function () {
FB.login(function (response) {
if (response.authResponse === "connected") {
$rootScope.$broadcast('fb_connected', {facebook_id:response.authResponse.userID});
} else {
$rootScope.$broadcast('fb_login_failed');
}
}, {scope: "read_insights, publish_pages, manage_pages"});
},
logout: function () {
FB.logout(function (response) {
if (response) {
$rootScope.$broadcast('fb_logout_succeded');
$location.path('/login');
} else {
$rootScope.$broadcast('fb_logout_failed');
}
});
}
};
angular.module("LoginCtrlModule", ["FacebookLogin"])
.controller("LoginCtrl", ["$scope", "$location", "LoginFactory", function ($scope, $location, LoginFactory) {
$scope.login = function () {
LoginFactory.login();
$scope.on("fb_connected", function () {
$location.path("/");
});
$scope.on("fb_login_failed", function(){
$location.path("/login");
});
}
}]);
app.run(function ($rootScope, $location, LoginFactory) {
window.fbAsyncInit = function () {
FB.init({
appId: '',
status: true,
cookie: true,
xfbml: true
});
};
(function (d) {
var js, id = 'facebook-jssdk', ref = d.getElementsByTagName('script')[0];
if (d.getElementById(id)) {
return;
}
js = d.createElement('script');
js.id = id;
js.async = true;
js.src = "//connect.facebook.net/en_US/all.js";
ref.parentNode.insertBefore(js, ref);
}(document));
});
I was following these posts:
AngularJS- Login and Authentication in each route and controller
http://www.sitepoint.com/implementing-authentication-angular-applications/
But the problem i am facing is different.
Thanks for the help.
I think the solution is to not call the FB.getLoginStatus() but rather have an object that contains the User information and the access token, each time I try to route I should check if the User info object is null, in case if its not null then call the check login status method and accordingly take the route or not to take the route.
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);
}
});
}
});
}
]);
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;
}
}
}]);
I've come to this problem were my view loads before $scope params are assigned and this is caused by $http service call taking some time before response is achived.
This leaves me with dropdown boxes being unsync with url params on page reload...
Is there anyway to reload these $scope params or wait til they get values before rendering the view? I would like the easiest solution to this as Im yet farily new to angularjs.
Just give me a hint if more info is needed!
Here's some of the code...
Route
angular.module('app', ['ngRoute', 'app.controller', 'app.service', 'app.filter'])
.config(['$routeProvider', function($routeProvider) {
$routeProvider
.when('/order/:id', {
templateUrl: '../../App_AngularJs/partials/specificOrder.htm',
controller: 'orderController',
reloadOnSearch: true
})
.when('/orderitem/:id', {
templateUrl: '../../App_AngularJs/partials/orderItem/orderItem.htm',
controller: 'orderItemController',
reloadOnSearch: true
})
.when('/', {
templateUrl: '../../App_AngularJs/partials/searchOrder.htm',
controller: 'ordersController',
reloadOnSearch: false
//Use some resolve here!? How!?
});
}
Controller
var orderContrl = angular.module('app.controller', ['angularTreeview', 'ui.bootstrap'])
.controller('ordersController', [
'$scope', '$routeParams', '$location', '$filter', '$modal', '$log', 'orderService',
function ($scope, $routeParams, $location, $filter, $modal, $log, orderService) {
init();
function init() {
$scope.searchtext = $routeParams.search || '';
$scope.page = $routeParams.page || 1;
$scope.take = $routeParams.take || 10;
$scope.status = $routeParams.status || -1;
$scope.group = $routeParams.group || -1;
$scope.type = $routeParams.type || -1;
$scope.category = $routeParams.category || -1;
$scope.selectedOrganisation = "Knoc LK";
getOrders(true);
getFilters(true);
}
function getFilters(reloadPage) {
orderService.queryOrderAllDropdown()
.then(function (response) {
$scope.orderGroup = response.OrderGroups;
$scope.orderStatus = response.OrderStatus;
$scope.orderType = response.OrderTypes;
$scope.orderPackageCategory = response.ProductPackageCategories;
$scope.orderAllCategory = response.ProductItemCategories;
//Sets type and shows different categories depending on type chosen
getCategory();
//Trying to reassign the values but still nothing...
if (reloadPage) {
angular.forEach($scope.orderStatus, function (value) {
if ($routeParams.status == value.ID)
$scope.status = value.ID;
});
//Trying to reassign the values but still nothing...
$scope.group = $scope.group;
}
},
function (errorMessage) {
$scope.error = errorMessage;
});
}
Service
angular.module('app.service', [])
.service('orderService', ['$http', '$q', function ($http, $q) {
this.queryOrderAllDropdown = function () {
var deferred = $q.defer();
$http({
type: 'GET',
url: 'GenericHandlers/HttpOrderService.ashx',
method: 'GetOrderAllDropdown',
headers: { 'Content-Type': 'text/plain' }
}).success(function (data) {
deferred.resolve(data);
}).error(function () {
deferred.reject("An error occured while fetching data");
});
return deferred.promise;
},
You need to use a Resolver to fetch the data from the backend. Adding a "resolve" to the $routeProvider will fetch the data before the controller takes control. Check out this blog post for a similar example.
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?