Nested Service promise not resolved in then() - angularjs

I got a service which first need to retrieve an accesstoken and then a User object. When this is done, further UI actions needs to be taken. The problem I have is that login().then() is invoked with a valid Session.accesstoken but a non-resolved promise for the Session.user. So I can't do any logic based on the user's info (e.g. role, etc.)
The following code I have:
In controllers.js (login() is invoked upon form credentials button click)
.controller('LoginController',
function($scope, SessionService) {
$scope.credentials = {
username : '',
password : ''
};
$scope.login =
function() {
SessionService.login($scope.credentials).then(function() {
console.info("succesfully logged in as user " + JSON.stringify(Session));
// further UI actions..
});
};
})
In services.js
.factory('SessionService',
function(User, Session, AuthorizationService) {
return {
login : function(credentials) {
return AuthorizationService.requestToken(credentials)
.then(function(token) {
console.info("Got token: " + token);
if (!!token) {
Session.create(token);
console.info("Fetching user...");
return User.get({
id : 'me'
});
} else {
throw (new Error("Could not log in"));
}
}).then(function(user) {
console.info("Got user: " + user);
if (!!user) {
Session.user = user;
} else {
throw (new Error("Could not fetch user"));
}
});
},
};
})
Here is the output of the console:
Got token: cf9c0eba82d872508f7dcc0b234f0d52 services.js:120
Fetching user...
services.js:124 Got user: [object Object]
services.js:132 succesfully logged in as user
{"token":"cf9c0eba82d772508f7dcc0b234f0d52","user":{"$promise":{},"$resolved":false}}

The User object is created by $resource and is not a promise; it is the actual object that will eventually be populated with the user info, but at this point of the code it contains nothing.
The solution is simple: from the first then do:
return User.get({...}).$promise;
The next then will be invoked with the user object as expected, but only when the data is actually fetched.

Related

Make facebook posts with ionic/angular app

I'm creating an app with the ionic framework and integrate the facebookConnectPlugin for login and making posts to a fan page.
The login part is working properly, I can see the user data and display on the app. The problem I'm having is when I try to make a post to facebook.
This is the complete controller that manage the login and the post
.controller('signupCtrl', function($scope, $state, $q, UserService, $ionicLoading){
// This is the success callback from the login method
var accesstoken;
var fbLoginSuccess = function(response){
if (!response.authResponse){
fbLoginError("Cannot find the authResponse");
return;
}
var authResponse = response.authResponse;
accesstoken = authResponse.accessToken;
getFacebookProfileInfo(authResponse).then(function(profileInfo){
// For the purpose of this example I will store user data on local storage
UserService.setUser({
authResponse: authResponse,
userID: profileInfo.id,
name: profileInfo.name,
email: profileInfo.email,
picture: "http://graph.facebook.com/" + authResponse.userID + "/picture?type=large"
});
$ionicLoading.hide();
}, function(fail){
// Fail get profile info
alert('profile info fail ' + fail);
});
};
// This is the fail callback from the login method
var fbLoginError = function(error){
alert('fbLoginError ' + error);
$ionicLoading.hide();
};
// This method is to get the user profile info from the facebook api
var getFacebookProfileInfo = function (authResponse){
var info = $q.defer();
facebookConnectPlugin.api('/me?fields=email,name&access_token=' + authResponse.accessToken, null,
function (response) {
console.log(response);
info.resolve(response);
},
function (response) {
console.log(response);
info.reject(response);
}
);
return info.promise;
};
//This method is executed when the user press the "Login with facebook" button
$scope.facebookSignIn = function(){
console.log('---> facebookSignIn');
facebookConnectPlugin.getLoginStatus(function(success){
if (success.status === 'connected'){
// The user is logged in and has authenticated your app, and response.authResponse supplies
// the user's ID, a valid access token, a signed request, and the time the access token
// and signed request each expire
//alert('getLoginStatus ' + success.status);
// Check if we have our user saved
var user = UserService.getUser('facebook');
if (!user.userID){
alert('UNO');
getFacebookProfileInfo(success.authResponse).then(function(profileInfo) {
// For the purpose of this example I will store user data on local storage
UserService.setUser({
authResponse: success.authResponse,
userID: profileInfo.id,
name: profileInfo.name,
email: profileInfo.email,
picture: "http://graph.facebook.com/" + success.authResponse.userID + "/picture?type=large"
});
//$state.go('menu.home');
}, function(fail){
// Fail get profile info
alert('profile info fail ' + fail);
});
}else{
//alert('DOS');
$state.go('menu.home');
var fan_token = 'EAAH1eElPgZBl1jwZCI0BADZBlrZCbsZBWF5ig29V1Sn5ABsxH1o4kboMhpjZBDfKtD1lfDK1dJLcZBI4gRBOF2XGjOmWMXD0I8jtPZA4xLJNZADarOGx8fiXBRZCTOaxwBLQEwRjsvaqTtb2DTCI0Qdo3haX6vqHlJoWMZD';
console.log('access token', accesstoken);
facebookConnectPlugin.api(
'/186259017448757/feed',
'POST',
{
access_token: fan_token,
'message':'HOLA!'
},
function(response){console.log(response);alert(response.id)}
)
}
}else{
// If (success.status === 'not_authorized') the user is logged in to Facebook,
// but has not authenticated your app
// Else the person is not logged into Facebook,
// so we're not sure if they are logged into this app or not.
alert('getLoginStatus ' + success.status);
$ionicLoading.show({
template: 'Logging in...'
});
// Ask the permissions you need. You can learn more about
// FB permissions here: https://developers.facebook.com/docs/facebook-login/permissions/v2.4
facebookConnectPlugin.login(['email', 'public_profile'], fbLoginSuccess, fbLoginError);
}
});
};
})
This is the part where I try to make a post to a facebook wall
facebookConnectPlugin.api('/181297057448657/feed', 'POST', {access_token: fan_token, 'message':'HOLA!'},
function(response){
console.log(response);
alert(response.id)
}
)
I went to https://developers.facebook.com/tools/explorer to generate the access tokens, tried with several tokens (user, fanpage, etc) but any seem to work. I've also used this code snippet provided in the facebook devs explorer tool
FB.api(
'/174801556748297/feed',
'POST',
{"message":"testing3"},
function(response) {
// Insert your code here
}
);
The result is always the same, I get a JSON error in the response object and no facebook post to the wall
Any help would be really appreciated!
PD: app ID's have been modified, that why they don't match. Same with the tokens and fan page ID
I used following in my app to post ,Reference https://github.com/ccoenraets/sociogram-angular-ionic
$scope.share = function () {
OpenFB.post('/me/feed', $scope.item)
.success(function () {
console.log("done");
$ionicLoading.hide();
$scope.item.message="";
$ionicLoading.show({ template: 'Post successfully on Facebook!', noBackdrop: true, duration: 2000 });
$scope.status = "This item has been shared from OpenFB";
})
.error(function(data) {
$ionicLoading.hide();
alert(data.error.message);
});
};
HTML
<div class="media-body">
<textarea placeholder="What's on your mind?" ng-model="item.message"></textarea>
</div>
<hr style="margin-top: -1px;" />
<div style="text-align:right;padding-right:20px;padding-bottom:10px;">
<button class="fb-login" ng-click="share()">Post to Facebook</button>
</div>

Ionic Facebook Connect

I'm pretty new to developing in Ionic and I'm trying to integrate my application with Facebook login. I found this tutorial https://ionicthemes.com/tutorials/about/native-facebook-login-with-ionic-framework and I did everything the way it was shown but I'm getting the following error
TypeError: Cannot read property 'getLoginStatus' of undefined
at Scope.$scope.facebookSignIn (controllers.js:547)
at $parseFunctionCall (ionic.bundle.js:21172)
at ionic.bundle.js:53674
at Scope.$eval (ionic.bundle.js:23228)
at Scope.$apply (ionic.bundle.js:23327)
at HTMLAnchorElement.<anonymous> (ionic.bundle.js:53673)
at HTMLAnchorElement.eventHandler (ionic.bundle.js:11841)
at triggerMouseEvent (ionic.bundle.js:2865)
at tapClick (ionic.bundle.js:2854)
at HTMLDocument.tapTouchEnd (ionic.bundle.js:2977)
My Code for the controller is as follows:
.controller('WalkthroughCtrl', function($scope, $state, $q, UserService, $ionicLoading) {
var fbLoginSuccess = function(response) {
if (!response.authResponse){
fbLoginError("Cannot find the authResponse");
return;
}
var authResponse = response.authResponse;
getFacebookProfileInfo(authResponse)
.then(function(profileInfo) {
// For the purpose of this example I will store user data on local storage
UserService.setUser({
authResponse: authResponse,
userID: profileInfo.id,
name: profileInfo.name,
email: profileInfo.email,
picture : "http://graph.facebook.com/" + authResponse.userID + "/picture?type=large"
});
$ionicLoading.hide();
$state.go('app.feeds-categories');
}, function(fail){
// Fail get profile info
console.log('profile info fail', fail);
});
};
// This is the fail callback from the login method
var fbLoginError = function(error){
console.log('fbLoginError', error);
$ionicLoading.hide();
};
// This method is to get the user profile info from the facebook api
var getFacebookProfileInfo = function (authResponse) {
var info = $q.defer();
facebookConnectPlugin.api('/me?fields=email,name&access_token=' + authResponse.accessToken, null,
function (response) {
console.log(response);
info.resolve(response);
},
function (response) {
console.log(response);
info.reject(response);
}
);
return info.promise;
};
//This method is executed when the user press the "Login with facebook" button
$scope.facebookSignIn = function() {
facebookConnectPlugin.getLoginStatus(function(success){
if(success.status === 'connected'){
// The user is logged in and has authenticated your app, and response.authResponse supplies
// the user's ID, a valid access token, a signed request, and the time the access token
// and signed request each expire
console.log('getLoginStatus', success.status);
// Check if we have our user saved
var user = UserService.getUser('facebook');
if(!user.userID){
getFacebookProfileInfo(success.authResponse)
.then(function(profileInfo) {
// For the purpose of this example I will store user data on local storage
UserService.setUser({
authResponse: success.authResponse,
userID: profileInfo.id,
name: profileInfo.name,
email: profileInfo.email,
picture : "http://graph.facebook.com/" + success.authResponse.userID + "/picture?type=large"
});
$state.go('app.feeds-categories');
}, function(fail){
// Fail get profile info
console.log('profile info fail', fail);
});
}else{
$state.go('app.home');
}
} else {
// If (success.status === 'not_authorized') the user is logged in to Facebook,
// but has not authenticated your app
// Else the person is not logged into Facebook,
// so we're not sure if they are logged into this app or not.
console.log('getLoginStatus', success.status);
$ionicLoading.show({
template: 'Logging in...'
});
// Ask the permissions you need. You can learn more about
// FB permissions here: https://developers.facebook.com/docs/facebook-login/permissions/v2.4
facebookConnectPlugin.login(['email', 'public_profile'], fbLoginSuccess, fbLoginError);
}
});
};
})
Thank You in Advance.

Firebase Angular OAuth with Google email scope, auth.$authWithOAuthPopup vs ref.authWithOAuthPopup

I'm successfully using Firebase's angular library to auth users against Facebook and Google, but I'm having troubling retrieving the user's email when using firebaseAuth's $authWithOAuthPopup.
Here's my login function.
var ref = new Firebase(FIREBASE_URL);
var auth = $firebaseAuth(ref);
loginGoogle: function () {
console.log('Logging in Google.');
return auth.$authWithOAuthPopup('google', function(error, user){
//TODO: Handle Failed login better
console.log('Google login failed');
console.log(error);
},{
scope: 'email'
});
};
This will pop up the google auth window and log in successfully. But, in the access permissions window, it doesn't request the 'email' scope access.
If I use ref.authWinOAuthPopup(...) instead of auth.$authWithOAithPopup(...) it does properly request the email perms, and delivers that info after auth.
Am I doing something wrong here? Or, is it an Angularfire bug that I should be reporting?
Angularfire v0.9.2.
After digging in further I found that the $firebaseAuth.$authWithOAuthPopup takes only two arguments (provider and options), whereas the Firebase.authWithOAuthPopup takes three (provider, onComplete callback, options). So, using refactoring to use the returned promise instead of passing in a callback function fixed the issue.
To be more clear, I'd like to add my snippets to show how to pass the e-mail scope to google oAuth.
As you mentioned, if you're using the promise to do the authentication instead of the callback you can pass the option for the email scope to the auth. call as second parameter and then the e-mail address will be available in the payload.
Please have a look at the following snippet:
$scope.loginGoogle = function() {
Auth.$authWithOAuthPopup("google", {scope: ['email']}).then(function(authData) {
// handle authData in onAuth handler in app.js in run method with Auth.$onAuth(function(authData) { ... });
console.log("Authenticated successfully with payload:", authData);
$location.path(REDIRECT_ROUTE);
})
.catch(function(err) {
$scope.err = errMessage(err);
});
};
function errMessage(err) {
var msg = "";
//return angular.isObject(err) && err.code? err.code : err + '';
switch (err.code) {
case "INVALID_EMAIL":
case "INVALID_PASSWORD":
case "INVALID_USER":
console.log("The specified user account email is invalid.");
msg = "E-mail or password is invalid.";
break;
/*
case "INVALID_PASSWORD":
console.log("The specified user account password is incorrect.");
break;
case "INVALID_USER":
console.log("The specified user account does not exist.");
break;
default:
// password or email empty;
console.log("Error logging user in:", err);*/
};
return msg;
}
angular.module('firebase.auth', ['firebase', 'firebase.utils'])
.factory('Auth', ['$firebaseAuth', 'fbutil', function($firebaseAuth, fbutil) {
return $firebaseAuth(fbutil.ref());
}]);
This is what I did and it worked for me
.config(function ($firebaseRefProvider) {
$firebaseRefProvider.registerUrl('https://****.firebaseio.com/');
})
Then in controller
loginWithGoogle: function(){
var ref = $firebaseRef.default;
ref.authWithOAuthPopup("google", function(error, authData) {
if (error) {
...
} else {
console.log(authData.google.email);
}
},{scope: 'email'});
}

Firebase Simple Login $logout function does not set user property to null

Per the documentation on the $logout method for $firebaseSimpleLogin, the API says clearly that
When logout is called, the $firebaseSimpleLogin:logout event will be fired, and the user >property on the object will be set to null.
However, it doesn't seem to work for me.
Here are snippets of my code:
var firebaseUrl = 'https://MYURL.firebaseio.com/';
var firebaseObject = new Firebase(firebaseUrl);
$rootScope.firebaseLoginObject = $firebaseSimpleLogin(firebaseObject, function(error, user) {
if (error) {
console.log(error);
} else if (user) {
console.log('User ID: ' + user.uid + ', Provider: ' + user.provider);
} else {
console.log('User is logged out');
// user is logged out
}
});
// Function for logging out.
$scope.logout = function() {
$rootScope.firebaseLoginObject.$logout();
console.log('i logged out successfully');
console.log($rootScope.firebaseLoginObject);
console.log($rootScope.firebaseLoginObject.user);
console.log($rootScope.user);
$location.path('/');
};
The output I get in the console after clicking "ng-click=logout()" shows that $rootScope.firebaseLoginObject is not set to null.
The problem is with $scope.logout. Your console.log statements are being executed before the $logout function has completed its work.
$scope.logout = function() {
$rootScope.firebaseLoginObject.$logout();
// This is not necessarily true. At this point, you may still be
// logged in since $logout can take an unknown amount of time to finish.
console.log('i logged out successfully');
console.log($rootScope.firebaseLoginObject);
console.log($rootScope.firebaseLoginObject.user);
console.log($rootScope.user);
$location.path('/');
};
To ensure you're logging the state of the firebaseLoginObject after $logout has finished, wait until the logout event has been broadcasted on $rootScope.
$rootScope.$on("$firebaseSimpleLogin:logout", function(event) {
// Log values here.
});

Angular rendering engine & Firebase profile retrieval

What I want:
firebase checks authentication of page load
firebase returns userID if logged in
my function returns the username associated with the user.Id
assign to the variable that represents the username
render!
All on page load
Current Behavior:
The following configuration retrieves the username but will only display the username once I click a login button I have made.For some reason even though I am currently logged in I must click the login button. I want a set up where if I am logged in the app will just know I am logged in from the start!
crossfitApp.controller('globalIdCtrl', ["$scope",'$q','defautProfileData','$timeout', function ($scope,$q,defautProfileData,$timeout) {
var dataRef = new Firebase("https://glowing-fire-5401.firebaseIO.com");
$scope.myFbvar =null;
$scope.authenticated={
currentUser: null,
avatarUrl: "",
emailAddress: "",
settings: "",
currentUserid: null,
};
function getProfile(userID,assignMe){
myprofile= new Firebase("https://glowing-fire-5401.firebaseio.com/profiles/"+userID+"/username");
myprofile.once('value', function(nameSnapshot) {
assignMe = nameSnapshot.val();
});
};
$scope.auth = new FirebaseSimpleLogin(dataRef, function(error, user) {
if (error) {
//Error
console.log ('error');
}
else if (user) {
//logged in
$timeout(function() {
getProfile(user.id,);
});
console.log('logged in');
$scope.authenticated.currentUserid = user.id ;
}
else {
// user is logged out
console.log('logged out');
$timeout(function() {
$scope.authenticated.currentUserid =null;
});
}
});
}]); //Global
In your else if( user ) logic, you forgot to put your scope var inside the $timeout, so it is being set properly, but Angular doesn't learn about it until the next time $apply is called (e.g. ng-click, ng-submit, etc).
Thus:
else if (user) {
//logged in
$timeout(function() {
getProfile(user.id,);
$scope.authenticated.currentUserid = user.id ; // moved into $timeout
});
console.log('logged in');
}
You can read more about why this matters here and here.

Resources