Passing variables from AngularJS service to controller with Google API? - angularjs

I'm trying to use the Google API Javascript client to utilize Google Login on my app, and then access the user's email address and contacts. I'm combining this with AngularJS, and I've read that it's best to make this its own Service.
Here is the code for the service so far:
.service('googleLogin', ['$http', '$rootScope', function ($http, $rootScope) {
var clientId = '{MY CLIENT KEY}',
apiKey = '{MY API KEY}',
scopes = 'https://www.googleapis.com/auth/userinfo.email https://www.google.com/m8/feeds',
domain = '{MY COMPANY DOMAIN}';
this.handleClientLoad = function () {
// Step 2: Reference the API key
gapi.client.setApiKey(apiKey);
gapi.auth.init(function () { });
window.setTimeout(checkAuth, 1);
};
this.checkAuth = function() {
gapi.auth.authorize({ client_id: clientId, scope: scopes, immediate: true, hd: domain }, this.handleAuthResult );
};
this.handleAuthResult = function(authResult) {
if (authResult && !authResult.error) {
gapi.client.load('oauth2', 'v2', function () {
var request = gapi.client.oauth2.userinfo.get();
request.execute(function (resp) {
console.log(userEmail);
});
});
}
};
this.handleAuthClick = function (event) {
// Step 3: get authorization to use private data
gapi.auth.authorize({ client_id: clientId, scope: scopes, immediate: false, hd: domain }, this.handleAuthResult );
return false;
};
}]);
I then call this from the controller with the following:
$scope.login = function () {
googleLogin.handleAuthClick();
};
Which works, and the API is called properly. But now, I'm not sure how to get data from the API through the Service. I need to get the user's email address, as well as a list of their contacts. I can't just have separate get functions that return these values, because it seems like the API client calls must be made in chains (for example, handleAuthResult is called as a parameter in handleAuthClick).
I've also tried setting them as $rootScope values, or just normal variables, but once the controller calls them they always come as undefined.
Am I approaching this correctly? How can I get these variables from the Google API, to the Service, to the Controller? Thanks.

I ended up using the promise/defer implementation from angularJS, documented here.
Here's the final service:
.service('googleLogin', ['$http', '$rootScope', '$q', function ($http, $rootScope, $q) {
var clientId = '{MY CLIENT ID}',
apiKey = '{MY API KEY}',
scopes = 'https://www.googleapis.com/auth/userinfo.email https://www.google.com/m8/feeds',
domain = '{MY COMPANY DOMAIN}',
userEmail,
deferred = $q.defer();
this.login = function () {
gapi.auth.authorize({ client_id: clientId, scope: scopes, immediate: false, hd: domain }, this.handleAuthResult);
return deferred.promise;
}
this.handleClientLoad = function () {
gapi.client.setApiKey(apiKey);
gapi.auth.init(function () { });
window.setTimeout(checkAuth, 1);
};
this.checkAuth = function() {
gapi.auth.authorize({ client_id: clientId, scope: scopes, immediate: true, hd: domain }, this.handleAuthResult );
};
this.handleAuthResult = function(authResult) {
if (authResult && !authResult.error) {
var data = {};
gapi.client.load('oauth2', 'v2', function () {
var request = gapi.client.oauth2.userinfo.get();
request.execute(function (resp) {
$rootScope.$apply(function () {
data.email = resp.email;
});
});
});
deferred.resolve(data);
} else {
deferred.reject('error');
}
};
this.handleAuthClick = function (event) {
gapi.auth.authorize({ client_id: clientId, scope: scopes, immediate: false, hd: domain }, this.handleAuthResult );
return false;
};
}]);
And how it's currently called in the controller:
var promise = googleLogin.login();
promise.then(function (data) {
console.log(data.email);
}, function (reason) {
console.log('Failed: ' + reason);
});

use this way :
public Credential authenticate() throws IOException {
try {
Credential credential = GoogleCredential.getApplicationDefault().createScoped(scopes);
System.out.println("Loaded the Application Default Credentials.");
return credential;
} catch (IOException e) {
// No need to do anything, we'll fall back on other credentials.
}

Related

How to redirect to other page after successful login in angularjs?

Hi I am developing one application in angularjs as front end and web api2 as backend. I am new to both angular and web api. I have completed login page in angularjs. As soon as my login successful i want to go to page called Index.cshtml located at Views/Home/index.cshtml. I am trying as below.
This is my controller code.
app.controller('Login', function ($scope, LoginService) {
$scope.Login = function () {
var sub = {
User_Name: $scope.User_Name,
User_Password: $scope.User_Password
};
var checkData = LoginService.Login(sub);
checkData.then(function (data) {
//want redirection
var url = '/Home/Index/';
window.location = url;
}, function (error) {
})
};
});
This is my module.js
var app;
(function () {
app = angular.module("Login", []);
})();
service.js
app.service("LoginService", function ($http) {
this.Login = function (sub) {
var credentials = { User_Name: sub.User_Name, User_Password: sub.User_Password };
$http.post('api/NCT_UsersLogin/', credentials).success(function (response) { });
}
});
I am trying using window.location but it is not working. May I get some help here? Any help would be appreciated. Thank you.
You can use $state.go(url)
Try this
app.controller('Login', function ($scope, LoginService, $state) {
$scope.Login = function () {
var sub = {
User_Name: $scope.User_Name,
User_Password: $scope.User_Password
};
var checkData = LoginService.Login(sub);
checkData.then(function (data) {
//want redirection
var url = '/Home/Index/';
$state.go(url);
}, function (error) {
})
};
});
Try this,
$window.location.href = '/Home/index.html';
also inject $window to your controller
app.controller('Login', function ($scope, LoginService,$window) {
$scope.Login = function () {
var sub = {
User_Name: $scope.User_Name,
User_Password: $scope.User_Password
};
var checkData = LoginService.Login(sub);
checkData.then(function (data) {
//want redirection
$window.location.href = '/Home/index.html';
}, function (error) {
})
};
});
Try to change this:
window.location = url;
in this:
window.open("http://www.example.com","_self");
You should redirect correctly.
You need to use $state.go('/home');
For ex:
$http.post('api/NCT_UsersLogin/', credentials).success(function (response) {
//if your response status is success then redirect to another page using
$state.go('/home');
});

Backand/Ionic - How to retrieve data of logged in user?

I am using Backand as my database and they provided a todo app on their github but it only retrieve all the data of the users. I only want to retrieve the data of the logged in user. How can I do this?
Somewhat, I want to modify it on my services.js:
service.all = function () {
return $http.get(getUrl());
};
I want to pass the object of the current user.
GitHub: https://github.com/backand/todos-with-users
There is a todo with users demo. https://github.com/backand/angular-yeoman-todos/tree/todo-with-users
Ultimately, my user service looks something like this...
angular.module('app.services')
.service('userService', ['Backand', '$state', '$q', '$http', 'authService', function(Backand, $state, $q, $http, authService) {
this.getUserByEmail = function(email) {
var resultsDeferred = $q.defer();
var requestDeferred = $http({
method: 'GET',
url: Backand.getApiUrl() + '/1/objects/users',
params: {
pageSize: 1,
pageNumber: 1,
filter: [{
fieldName: 'email',
operator: 'equals',
value: email
}],
sort: ''
}
});
requestDeferred.then(function(response) {
var user = response.data.data;
resultsDeferred.resolve(user); // allow the controller to render
}, function failureCallback(reason) {
resultsDeferred.reject(reason); // bubble up failure
});
return resultsDeferred;
}
}]);
You get the email from ...
var user = Backand.getUserDetails();
var userEmail = null;
if (user.$$state.value !== null) {
userEmail = user.$$state.value.username;
} else {
userEmail = null;
}
var userDeferred = userService.getUserByEmail($scope.userEmail).promise;
userDeferred.then(function successCallback(response) {
$scope.user = response[0];
console.log('userId (email): %o', $scope.user.id);
console.log('user phone: %o', $scope.user.phone);
});

Injecting service with $http messes up controller

I made a service that's using $http to post login data and get authentication token, but whenever i inject it into the controller, it breaks (looks like html doesnt see it). When I remove the service injection, or inject one using $resource instead, everything works fine.
Here's the code for the service:
MyApp.service('LoginSrv', ['$http', function User($http) {
var userData = {
isAuthenticated: false,
username: '',
bearerToken: '',
expirationDate: null,
};
function setHttpAuthHeader() {
$http.defaults.headers.common.Authorization = 'Bearer ' + userData.bearerToken;
}
this.getUserData = function(){
return userData;
};
this.authenticate = function(username, password, successCallback, errorCallback) {
var config = {
method: 'POST',
url: '/accounts/login',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
},
data: 'grant_type=password&username=' + username + '&password=' + password,
};
$http(config)
.success(function(data) {
userData.isAuthenticated = true;
userData.username = data.userName;
userData.bearerToken = data.access_token;
userData.expirationDate = new Date(data['.expires']);
setHttpAuthHeader();
if (typeof successCallback === 'function') {
successCallback();
}
})
.error(function(data) {
if (typeof errorCallback === 'function') {
if (data.error_description) {
errorCallback(data.error_description);
} else {
errorCallback('Unable to contact server; please, try again later.');
}
}
});
};
}]);
And here is the controller code:
MyApp.controller('mainCtrl', function ($scope, LoginSrv)
{
$scope.loginUsername = 'Jan';
$scope.loginPassword = 'Maria';
$scope.userLogin = new LoginSrv();
$scope.loginError = false;
function onSuccesfulLogin () {};
function onFailedLogin(error) {};
$scope.login = function () {
userLogin.authenticate($scope.loginUsername, $scope.loginPassword, onSuccesfulLogin, onFailedLogin);
};
});
Services are singleton so you need not give a "new",
I Made a brief example of the same flow you need and worked well, I hope to help:
The Service
angular.module("yourapp").factory('LoginSrv', function User($http) {
var _authenticate = function(username, password) {
console.log('logged')
};
return {
authenticate: _authenticate
};
});
The Controller
angular.module("yourapp").controller('mainCtrl', function ($scope, $http, LoginSrv)
{
$scope.loginUsername = 'Jan';
$scope.loginPassword = 'Maria';
$scope.userLogin = LoginSrv;
$scope.loginError = false;
$scope.login = function () {
userLogin.authenticate($scope.loginUsername, $scope.loginPassword);
};
});
The other answer does a good job of explaining your LoginSrv related exception and explains how implement a service/factory. However what it fails to note is the differences between the two.
Factory
When injecting a factory you will be provided with the return value as a result of invoking the factory function.
Service
When injecting a service you will be provided with an instance of the service function. That is akin to new serviceFunction();. It is important to note angular will do this the first time the service is injected, all others times it is injected you will receive the same instance.
So Factories are meant for object creation (hence the name) and services are meant, well, for services. So shared logic.
So in my opinion (that's all it is) your existing service is trying to do both. You appear to have a user object that your wanting to create but also methods for authenticating a user. It would be best to put that user object in a factory which returns a create method to create a new user. Then put the authentication logic in a service. Then your authentication is not directly coupled to your user implementation.
Possible Implementation (pseudo code)
.factory('userFactory', function () {
return {
create: function (details) {
return Object.create({}, {
username: {
value: details.username
},
password: {
value: details.password
},
isAuthenticated: {
value: false
}
});
}
}
});
.service('auth', function ($http) {
this.authenticate = function (username, password) {
//Build config
return $http();
}
});
.controller('test', function ($scope, userFactory, auth) {
var user = userFactory.create({
username: 'hiya',
password: 'epic secrets'
});
auth.authenticate(user.username, user.password)
.then(function (d) {
user.isAuthenticated = d.isAuthenticated;
})
.catch(SomeGenericErrorHandler);
});
any questions just ask

How to get the data for my controller when http request in progress?

I have following controller
1) introCtrl
2) ArticleCtrl
3) articleService (Service)
Now I am sending an http request from introCrtl
.controller('IntroCtrl', function($scope, articleService) {
articleService.getArticles();
});
and AricleCtrl is
.controller('ArticleCtrl', function($scope,$rootScope,articleService) {
$scope.articles = articleService.fetchArticles();
})
and my Service is
.service('articleService', function ($http, $q) {
var articleList = [];
var getArticles = function() {
$http({
url: "muylink,co,",
data: { starLimit: 0, endLimit: 150,created_date: 0 },
method: 'POST',
withCredentials: true,
}).success(function (data, status, headers, config) {
articleList.push(data);
}).error(function (err) {
console.log(err);
})
};
var fetchArticles = function() {
return articleList[0];
}
return {
getArticles: getArticles,
fetchArticles: fetchArticles
};
});
Which is also working fine. Now Problem is that
Sometimes my http request sending respone late and i got nothing in
$scope.articles.
Can we implement watch here. How i need to implement $watch here. I dont want to implement promise. because i want to run http request behind the scene.
Thanks
It would be better if you switch to a state based setup with ui-router that way you can do this :
$stateProvider.state('myState', {
url: 'the/url/you/want',
resolve:{
articleService: 'articleService' // you are dependency injecting it here,
articles: function (articleService) {
return articleService.getArticles.$promise;
}
},
controller: 'IntroCtrl'
})
// then your controller can just inject the articles and they will be resolved before your controller loads so you it will always be fetched prior
.controller('IntroCtrl', function($scope, articles) {
$scope.articles = articles;
});
for more information take a look at this
ui-router info
All to do is set watch on articleList and provide maintaining function.
As you are watching array, it's good to change it to string.
Create function in watch which results array.
$scope.$watch( function() {
return JSON.stringify($scope.articleList);
}, function(newVal,oldVal){
//provide logic here
});
If your service result is asynchron (like http requests) you should return promises from your service.
.controller('ArticleCtrl', function($scope,$rootScope,articleService) {
articleService.fetchArticles().then(function(articles) {
$scope.articles = articles;
});
})
Service
// not sure about your service logic... simplified:
.service('articleService', function ($http, $q) {
var articleListPromise ;
var getArticles = function() {
articleListPromise = $http(/* ...*/);
};
var fetchArticles = function() {
return articleListPromise.then(function(data) {
return data[0];
});
}
return {
getArticles: getArticles,
fetchArticles: fetchArticles
};
});

Resolving a promise in a dependent AngularJS service

I have a simple AngularJS app running in a Chrome Extension making use of the Storage API. Having an issue with the async nature of Storage; I've abstracted the storage away into a 'UserService' that sets and gets the data as a factory:
app.factory('UserService',
function($q, AppSettings) {
var defaults = {
api: {
token: AppSettings.environments[1].api.token
},
email: ''
};
var service = {
user: {},
save: function() {
chrome.storage.sync.set({'user': angular.toJson(service.user)});
},
restore: function() {
var deferred = $q.defer();
chrome.storage.sync.get('user', function(data) {
if(!data) {
chrome.storage.sync.set({'user': defaults});
service.user = defaults;
} else {
service.user = angular.fromJson(data.user);
}
deferred.resolve(service);
});
return deferred.promise;
}
};
// set the defaults
service.restore().then(function(data) {
console.log(data);
return data;
});
});
The console.log() call above dumps out the data as expected. However, when I am including the UserService in other factories (I have an APIService that makes use of a user-specific API token), the UserService parameter is being flagged as 'undefined' in the code below:
app.factory('APIService',
function($resource, $http, UserService, AppSettings) {
var token = UserService.user.api.token;
...
});
I am sure I am not fully grasping the Angular promise pattern in terms of consuming resolved promises throughout the app.
Updated code:
app.factory('UserService',
function($q, AppSettings) {
var defaults = {
api: {
token: AppSettings.environments[1].api.token
},
email: ''
};
var service = {
user: {},
save: function() {
chrome.storage.sync.set({'user': angular.toJson(service.user)});
},
restore: function() {
var deferred = $q.defer();
chrome.storage.sync.get('user', function(data) {
if(!data) {
chrome.storage.sync.set({'user': defaults});
service.user = defaults;
} else {
service.user = angular.fromJson(data.user);
}
deferred.resolve(service.user);
});
return deferred.promise;
}
};
// set the defaults
service.restore().then(function(data) {
console.log(data);
return data;
});
return service;
});
Edit/Additional Info:
Ok, getting close. Have refactored so that I am returning the object properly, but the issue now is that when the APIService gets created and tries to use the properties of the UserService object, they simply don't exist yet as they are only created after the async restore method is resolved. So it's not possible to access the UserService.user.api.token property, as it doesn't exist at that point, so the question is, how do I get that data in APIService when I need it if it is not available at that point? I'm trying to avoid having to put the entire contents of APIService into a callback that fires after a hypothetical new UserService.get() method that calls the callback on resolution of the promise. Any final guidance appreciated.
Your service is wrong. Please look at my fix:
app.factory('UserService',
function($q, AppSettings) {
var defaults = {
api: {
token: AppSettings.environments[1].api.token
},
email: ''
};
var service = {
user: {},
save: function() {
chrome.storage.sync.set({'user': angular.toJson(service.user)});
},
restore: function() {
var deferred = $q.defer();
chrome.storage.sync.get('user', function(data) {
if(!data) {
chrome.storage.sync.set({'user': defaults});
service.user = defaults;
} else {
service.user = angular.fromJson(data.user);
}
deferred.resolve(service.user); // <--- return the user in here
});
return deferred.promise;
}
};
// set the defaults
service.restore().then(function(data) {
console.log(data);
return data;
});
return service; // <--- return the service to be used injected when injected
});
[EDIT]
answer to your new question: Dont access user directly. create a new function in your service like getUser() that returns a promise. In that function return the user if it is already retreived otherwise return the restore() function:
var service = {
user: null,
getUser: function() {
if (service.user)
{
var deferred = $q.defer();
deferred.resolve(service.user);
return deferred.promise;
}
else
return service.restore();
},
save: function() {
chrome.storage.sync.set({'user': angular.toJson(service.user)});
},
restore: function() {
var deferred = $q.defer();
chrome.storage.sync.get('user', function(data) {
if(!data) {
chrome.storage.sync.set({'user': defaults});
service.user = defaults;
} else {
service.user = angular.fromJson(data.user);
}
deferred.resolve(service.user); // <--- return the user in here
});
return deferred.promise;
}
};
You're not returning an object from your factory. So when you try to inject your UserService parameter, it gives undefined because you haven't returned anything from your UserService function.
If you return your service variable, I think you'll get the behavior you're looking for.

Resources