Getting to work google API then() function on an AngularJS app - javascript

google-api-javascript
I was following these instructions to start using google api on an angularjs application:
quickstart
And here's my attempt:
lib/init.js
function initClient() {
console.log('-- initClient');
var resp = gapi.client.init({
apiKey: '',
clientId: '',
discoveryDocs: [
"https://www.googleapis.com/discovery/v1/apis/calendar/v3/rest"
],
scope: [
'profile',
'https://www.googleapis.com/auth/calendar.readonly'
]
}).then(function() {
console.log('-- then') // never executes
// api loaded
}, function(error) {
console.log('ERROR: ' + error);
});
}
function handleClientLoad() {
console.log('-- handleClientLoad');
gapi.load('client:auth2', {
callback: function() {
initClient();
},
onerror: function() {
console.log('gapi failed to load!');
},
timeout: 5000, // 5 seconds.
ontimeout: function() {
// Handle timeout.
console.log('gapi.client could not load in a timely manner!');
}
});
}
index.html
<!doctype html>
<html>
<head>
<link rel="stylesheet" href="lib/style.css">
</head>
<body ng-app="my-app">
<div ng-controller="MainCtrl">
<h3>Testing Google JS API Lib</h3>
</div>
<script type="text/javascript" src="lib/init.js"></script>
<script id="gapiscript" src="https://apis.google.com/js/platform.js?onload=handleClientLoad">
</script>
<script src="lib/script.js"></script>
</body>
</html>
gapi.client.load().then() {...} is not executing and the console.log('-- then') debug line never shows. Doing document.getElementbyId('gapiscript') I get:
<script id="gapiscript" src="https://apis.google.com/…?onload=handleClientLoad" gapi_processed="true">
console.log('-- handleClientLoad'); and console.log('-- initClient'); are being executed and show in the console. I get no error message from the onerror callback.
Another console message I'm getting is CSI/start cb=gapi.loaded_0:613:127
How can I get the then() function to execute to start using the api?

In my case gapi.load accepts only single callback function:
gapi.load('client:auth2',
function() {
initClient();
}
);

Related

Keycloak login returns 404 using JavaScript adapter

I'm using Keycloak's bower package to create a very basic demo HTML/JS app. I have Keycloak running locally and keycloak.init() seems to work (no error triggered). However when I call keycloak.login() a 404 is returned. Might the login URL be wrongly created by the adapter?
The URL returned by keycloak.createLoginUrl() is
https://<keycloak url>/realms/<realm>/protocol/openid-connect/auth?client_id=account&redirect_uri=file%3A%2F%2F%2FUsers%2Fjgallaso%2FProjects%2Fdemos%2Fkeycloak-simple-web-client%2Findex.html&state=b167dc0b-3e5b-4c67-87f7-fd5289fb7b8f&nonce=1e2cb386-51db-496a-8943-efcf4ef5d5e1&response_mode=fragment&response_type=code&scope=openid
And this is my entire code:
<head>
<script src="bower_components/keycloak/dist/keycloak.min.js"></script>
</head>
<body>
<button id="login">Login</button>
</body>
<script>
var keycloak = Keycloak({
url: 'https://keycloak-keycloak.192.168.37.1.nip.io',
realm: 'demo',
clientId: 'account'
});
keycloak.init()
.success(authenticated => {
document.getElementById("login")
.addEventListener("click", () => { keycloak.login(); });
}).error(err => {
console.log("init, error: " + err);
});
</script>
</head>
Response is a plain:
ERROR 404: Not Found
You have 2 posibilities :
invoque login automatically in init method
login manually after call init without params
1)
<head>
<script src="bower_components/keycloak/dist/keycloak.min.js"></script>
</head>
<body>
<button id="login">Login</button>
</body>
<script>
var keycloak = Keycloak({
url: 'https://keycloak-keycloak.192.168.37.1.nip.io',
realm: 'demo',
clientId: 'account'
});
keycloak.init('login-required')
.success(function(authenticated) => {
}).error(err => {
console.log("init, error: " + err);
});
</script>
</head>
2)
keycloak.init().success(function(authenticated) {
if(authenticated == true){
alert('usuario logeado');
}else{
alert('usuario no logeado');
keycloak.login();
}
}).error(function() {
alert('failed to initialize');
});
I had trouble trying directly from the management.
file://c:/example.html
To do a better test you should leave your index.html on a local test server.
What I did was install the web server plugin for chrome and it worked for me.
I hope it'll help you.
regards

Google SignIn for the first time - immediate_failed

I'm using this script to sign in the user to my webapp.
<script src="https://apis.google.com/js/platform.js" async defer></script>
<script src="https://apis.google.com/js/api:client.js"></script>
<script>
var googleUser = {};
var startApp = function() {
gapi.load('auth2', function(){
auth2 = gapi.auth2.init({
client_id: '0000000000000-xxxx00000xxxx0000xxxx0000xxxx000.apps.googleusercontent.com',
cookiepolicy: 'single_host_origin',
scope: 'profile',
});
attachSignin(document.getElementById('login-with-google'));
attachSignin(document.getElementById('register-with-google'));
});
};
function attachSignin(element) {
console.log(element.id);
auth2.attachClickHandler(element, {},
function(googleUser) {
onSignIn(googleUser);
},
function(error) {
console.log(JSON.stringify(error, undefined, 2));
if(error['error'] && error['error'] == 'IMMEDIATE_FAILED'){
auth2 = gapi.auth2.getAuthInstance();
auth2.signIn(
{
immediate: false
}
);
console.log('authorize--end');
}
});
}
</script>
After the user clicks on the button I am getting IMMEDIATE_FAILED
{
"type": "tokenFailed",
"idpId": "google",
"error": "IMMEDIATE_FAILED"
}
I saw some posts here on SO saying that if the user has never connected to my webapp, I have to call SignIn for a second time and the result is
{
error: "popup_closed_by_user"
}
Seriously, I don't know what I'm doing wrong, but clearly I'm doing something very stupid.
It was an error on JS API.
See more
https://github.com/google/google-api-javascript-client/issues/305

JQuery nested $.when ... then statements

I'm trying to do a FreeCodeCamp exercise where I call the Twitch TV API. For each channel I make a call to the API to get the channel data. I then make a subsequent call to get the streaming data. The call for the channel information is wrapped in a $.when ... then loop and seems to work fine. I then added a second call to get the stream data and code does not seem to wait for that call to complete.
$(document).ready(function() {
'use strict';
var dataArray = []; // This holds all the channels that I find. I then sort to get the most popular first.
$.when(
// TODO this should be some sort of loop to make it more flexible
getChannelData("https://api.twitch.tv/kraken/search/channels?api_version=3&q=all&limit=10&offset=0&callback=?") // First 10
).then(function() {
sortData();
displayData();
});
function getChannelData(channelStatement) {
return $.getJSON(channelStatement, function(channelData) {
channelData.channels.forEach(function(element) {
// Get stream details
var channel;
channel = {
logo: (element.logo === null) ? "https://upload.wikimedia.org/wikipedia/commons/3/33/White_square_with_question_mark.png" : element.logo, // Channel: Url for image
displayName: element.display_name, // Channel: Broadcaster name
channelName: element.name, // Channel: Channel name
url: element.url, // Channel: Used to generate link to twitch page
game: element.game, // Channel: As the name suggests
status: element.status, // Chaneel: Description of the stream
views: element.views, // Channel: As the name suggests
onLine: true
};
//dataArray.push(channel);
var streamUrl = "https://api.twitch.tv/kraken/streams/" + element.name + "?api_version=3&callback=?";
$.when(
getStreamData(streamUrl, channel)
)
.then(function() {
dataArray.push(channel);
});
}); // channel data forEach
});
}
function getStreamData(streamUrl, channel) {
return $.getJSON(streamUrl, function(stream) {
channel.onLine = (stream.stream === null) ? false : true;
});
}
function sortData() {
}
function displayData() {
}
}); // end ready
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css" integrity="sha384-1q8mTJOASx8j1Au+a5WDVnPi2lkFfwwEAa8hDDdjZlpLegxhjVME1fgjWPGmkzs7"
crossorigin="anonymous">
<link href='https://fonts.googleapis.com/css?family=Roboto' rel='stylesheet' type='text/css'>
<link rel="stylesheet" type="text/css" href="style.css">
<title>Twitch TV</title>
</head>
<body>
<div class="container-fluid">
<ol id="twitchList">
</ol>
</div>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.2.0/jquery.min.js"></script>
<script src="TwitchTV.js"></script>
</body>
</html>
This JSBin link shows the code. Line 51 shows the second ajax call.
The code should populate an array and then on completion of all the calls display the data. I appreciate that with a production system waiting for all these calls to complete will lead to a less than idea user experience.
You need to re-oganize it slightly so that the promise you return from getChannelData is waiting on the promises created within your forEach.
function getChannelData(channelStatement) {
return $.getJSON(channelStatement).then(function(channelData) {
return $.when.apply(null, channelData.channels.map(function(element) {
// Get stream details
var channel = {
logo: (element.logo === null) ? "https://upload.wikimedia.org/wikipedia/commons/3/33/White_square_with_question_mark.png" : element.logo, // Channel: Url for image
displayName: element.display_name, // Channel: Broadcaster name
channelName: element.name, // Channel: Channel name
url: element.url, // Channel: Used to generate link to twitch page
game: element.game, // Channel: As the name suggests
status: element.status, // Chaneel: Description of the stream
views: element.views, // Channel: As the name suggests
onLine: true
};
//dataArray.push(channel);
var streamUrl = "https://api.twitch.tv/kraken/streams/" + element.name + "?api_version=3&callback=?";
return getStreamData(streamUrl, channel);
}); // channel data map
});
}
you would then use it like:
getChannelData(whatever).then(function () {
console.log(arguments); // this is dataArray
});

How do you attach a picture to LinkedIn share from a blog post?

I have a blog that I want to make shareable via LinkedIn. The docs LinkedIn presents, while simply stated don't have enough detail for me to understand my use case. My use case requires me to dynamically put the picture and description in each blog post, which isn't being populated right now. This is an Angular project.
My current code:
post.html
<script>
delete IN;
$.getScript("https://platform.linkedin.com/in.js");
</script>
<script type="IN/Share" data-url={{webAddress}} data-counter="right"></script>
post.js
//I have all of my data in $scope variables in this area, which includes
// the picture and description I'd like to attach to the post.
Here is what the LinkedIn docs show as the right way to do this:
post.html
<script type="text/javascript" src="//platform.linkedin.com/in.js">
api_key: YOUR_API_KEY_HERE
authorize: true
onLoad: onLinkedInLoad
</script>
<script type="text/javascript">
// Setup an event listener to make an API call once auth is complete
function onLinkedInLoad() {
IN.Event.on(IN, "auth", shareContent);
}
// Handle the successful return from the API call
function onSuccess(data) {
console.log(data);
}
// Handle an error response from the API call
function onError(error) {
console.log(error);
}
// Use the API call wrapper to share content on LinkedIn
function shareContent() {
// Build the JSON payload containing the content to be shared
var payload = {
"comment": "Check out developer.linkedin.com! http://linkd.in/1FC2PyG",
"visibility": {
"code": "anyone"
}
};
IN.API.Raw("/people/~/shares?format=json")
.method("POST")
.body(JSON.stringify(payload))
.result(onSuccess)
.error(onError);
}
</script>
As I understand it, I need to populate the payload object with the right data/links. I have no clue how to do this based on what's in the docs.
Here are a few things I've tried/thought about along with where I'm currently stuck:
1) Get the data from post.js and put it in the payload object between the script tags in post.html. After doing some research, it is not possible to do this. Though I welcome being corrected if I'm wrong.
2) Bring the IN object into angular and populate the payload in post.js. This sounds really great but LinkedIn provides no html with which to call a function in post.js with Angular. Plus the LinkedIn code as presented takes care of formatting for the button and what comes after you click it.
3) Make an http call inside the script tags with JQuery. I rarely if ever use JQuery and have never used http for JQuery before. If this is even a feasible way to think of this problem, this is what I came up with:
<script type="IN/Share" data-url={{webAddress}} data-counter="right">
$.get( "https://public-api.wordpress.com/rest/v1.1/sites/myPost", function( response ) {
var post = _.first(_.filter(response.posts, function(n){return n.title.replace(/ /g,"-").replace(/[:]/g, "").toLowerCase() === $stateParams.id}));
var post1 = _.assign(post, {category: _.first(_.keys(post.categories)), pic: _.first(_.values(post.attachments)).URL, credit: _.first(_.values(post.attachments)).caption, linkCredit: _.first(_.values(post.attachments)).alt, fullStory: post.content.replace(/<(?!\s*\/?\s*p\b)[^>]*>/gi,'')});
**var image = post1.pic;**
**var title = post1.title;**
**var webAddress = window.location.href;**
function onLinkedInLoad() {
IN.Event.on(IN, "auth", shareContent);
}
function onSuccess(data) {
console.log(data);
}
function onError(error) {
console.log(error);
}
function shareContent(title, image, webAddress) {
var payload = {
"content": {
"title": title,
"submitted-image-url": image,
"submitted-url": webAddress
}
};
IN.API.Raw("/people/~/shares?format=json")
.method("POST")
.body(JSON.stringify(payload))
.result(onSuccess)
.error(onError);
}
});
</script>
This solution did not result in a solution either. Where to go from here, I have no ideas. I'm sure this simple but idiosyncratic enough that I need a little hand holding.
Unfortunately, I have not worked with linkedin API.
Perhaps not all will be right in my example. But I've got to use a variable IN in angular and write about the call API wrapper.
An example of the use of plugins, see page LinkedIn Plugins.
Live example on jsfiddle.
//CallBackHell
function LinkedInServiceFunc(callback) {
callback && IN.Event.onDOMReady(callback);
}
angular.module('ExampleApp', [])
.controller('ExampleController', function($scope, LinkedInService, ShareLinkedINService) {
console.log('ExampleController IN', IN);
console.log('ExampleController LinkedInService', LinkedInService);
LinkedInService.promise.then(function(LIN) {
console.log('Complete loading script for LinkedIn in ExampleController', LIN.Objects)
});
//Then you can interact with IN object as angular service. Like this
$scope.shareContent = function() { // Use the API call wrapper to share content on LinkedIn
// Build the JSON payload containing the content to be shared
var payload = {
"comment": $scope.comment,
"visibility": {
"code": 'anyone'
}
};
// Handle the successful return from the API call
function onSuccess(data) {
console.log(data);
}
// Handle an error response from the API call
function onError(error) {
console.log(error);
}
console.log('shareContent', payload);
LinkedInService.promise.then(function(LIN) {
LIN.API.Raw("/people/~/shares?format=json")
.method("POST")
.body(JSON.stringify(payload))
.result(onSuccess)
.error(onError);
});
}
$scope.shareContentService = function() {
//It's better way, i think
ShareLinkedINService.shareContent($scope.comment, 'anyone').then(function(data) {
console.log('success', data);
}).catch(function(data) {
console.err('error', data);
});
}
})
.service('LinkedInService', function($q) {
var defer = $q.defer();
LinkedInServiceFunc(function() {
defer.resolve(IN);
});
return {
promise: defer.promise
};
})
//You can create wrapper on IN API
.service('ShareLinkedINService', function(LinkedInService, $q) {
return {
shareContent: function(comment, visible) {
var defer = $q.defer();
var payload = {
"comment": comment,
"visibility": {
"code": visible
}
};
LinkedInService.promise.then(function(LIN) {
LIN.API.Raw("/people/~/shares?format=json")
.method("POST")
.body(JSON.stringify(payload))
.result(defer.resolve)
.error(defer.reject);
});
return defer.promise;
}
}
})
.directive('linkedInShareButton', function(LinkedInService) {
return {
restrict: "E",
replace: false,
scope: {
shareUrl: "#",
counter:"#"
},
link: function(scope, elem, attr) {
var script = document.createElement('script');
script.setAttribute('type', 'IN/Share');
script.setAttribute('data-url', scope.shareUrl);
script.setAttribute('data-counter', scope.counter);
elem.append(script);
},
};
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.8/angular.min.js"></script>
<script type="text/javascript" src="//platform.linkedin.com/in.js">
authorize: false
onLoad: LinkedInServiceFunc
//I don't have api_key, because i delete it
// api_key: YOUR_API_KEY_HERE
// authorize: true
// onLoad: onLinkedInLoad
</script>
<body ng-app="ExampleApp">
<div>
<div ng-controller="ExampleController">
<input ng-model="comment">
<button ng-click="shareContent()">
shareContent
</button>
<button ng-click="shareContentService()">
shareContentService
</button>
<script type="IN/Share" data-url="www.mail.ru" data-counter="top"></script>
<linked-in-share-button share-url="www.mail.ru" counter="top"></linked-in-share-button>
</div>
</div>
</body>

Use custom image for google signin button

I want to provide users a facility to sign in with google. However, I want to use my image(only image, no css) as "sign in with google" button. I am using the following code:
<div id="mySignin"><img src="images/google.png" alt="google"/></div>
I am also using gapi.signin.render function as mentioned on google developer console. The code is:
<script src="https://apis.google.com/js/client:platform.js" type="text/javascript"></script>
<script>
function render(){
gapi.signin.render("mySignIn", {
// 'callback': 'signinCallback',
'clientid': 'xxxx.apps.googleusercontent.com',
'cookiepolicy': 'single_host_origin',
'requestvisibleactions': 'http://schema.org/AddAction',
'scope': 'profile'
});
}
The problem is that google signin popup is not opening and I cannot figure out how to solve it. Please suggest. Thanks in advance.
<script type="text/JavaScript">
/**
* Handler for the signin callback triggered after the user selects an account.
*/
function onSignInCallback(resp) {
gapi.client.load('plus', 'v1', apiClientLoaded);
}
/**
* Sets up an API call after the Google API client loads.
*/
function apiClientLoaded() {
gapi.client.plus.people.get({userId: 'me'}).execute(handleEmailResponse);
}
/**
* Response callback for when the API client receives a response.
*
* #param resp The API response object with the user email and profile information.
*/
function handleEmailResponse(resp) {
var primaryEmail;
var jsonobj=JSON.stringify(resp);alert(jsonobj);
var uid= jsonobj.id;
var user_name1= jsonobj.name;
for (var i=0; i < resp.emails.length; i++) {
if (resp.emails[i].type === 'account') primaryEmail = resp.emails[i].value;
}
/* document.getElementById('response').innerHTML = 'Primary email: ' +
primaryEmail + '<br/>id is: ' + uid; */
}
To use an image as your "Google Sign-in" button, you can use the GoogleAuth.attachClickHandler() function from the Google javascript SDK to attach a click handler to your image. Replace <YOUR-CLIENT-ID> with your app client id from your Google Developers Console.
HTML example:
<html>
<head>
<meta name="google-signin-client_id" content="<YOUR-CLIENT-ID>.apps.googleusercontent.com.apps.googleusercontent.com">
</head>
<body>
<image id="googleSignIn" src="img/your-icon.png"></image>
<script src="https://apis.google.com/js/platform.js?onload=onLoadGoogleCallback" async defer></script>
</body>
</html>
Javascript example:
function onLoadGoogleCallback(){
gapi.load('auth2', function() {
auth2 = gapi.auth2.init({
client_id: '<YOUR-CLIENT-ID>.apps.googleusercontent.com',
cookiepolicy: 'single_host_origin',
scope: 'profile'
});
auth2.attachClickHandler(element, {},
function(googleUser) {
console.log('Signed in: ' + googleUser.getBasicProfile().getName());
}, function(error) {
console.log('Sign-in error', error);
}
);
});
element = document.getElementById('googleSignIn');
}
For those who come to here trying to get the button work out: The code below should do the trick.
It looks like the 'callback' method doesn't seem to work not sure if this is something to do with Vue as I am building it on Vue, or Google changed it as this was posted 5 years ago. Anyways, use the example below.
window.onload =() =>{
var GoogleUser = {}
gapi.load('auth2',() =>{
var auth2 = gapi.auth2.init({
client_id: '<client-unique>.apps.googleusercontent.com',
cookiepolicy: 'single_host_origin',
scope: 'profile'
});
auth2.attachClickHandler(document.getElementById('googleSignup'), {},
(googleUser) =>{
console.log('Signed in: ' + googleUser.getBasicProfile().getName());
},(error) =>{
console.log('Sign-in error', error);
}
);
});
}
Change 'client_id' to your client id, and element id to your customized button id.
I hope this saves time for anyone!
Plus: I ended up using the code below, which is clearer:
window.onload = () => {
gapi.load('auth2', () => {
let auth2 = gapi.auth2.init({
client_id: '<client_id>.apps.googleusercontent.com',
cookiepolicy: 'single_host_origin',
scope: 'profile email'
});
document.getElementById('googleSignup').addEventListener('click',() => {
auth2.signIn().then(() => {
let profile = auth2.currentUser.get().getBasicProfile();
... profile functions ...
}).catch((error) => {
console.error('Google Sign Up or Login Error: ', error)
});
});;
});
}

Categories

Resources