16

I followed this link https://developers.google.com/identity/sign-in/web/sign-in to get Google Signin on Angular-based website.

I have seen some weird behavior. The signin button sometimes show but not always. When I refresh a page, only 1 in 5 refreshes, the button appears.

I tried in Chrome and Safari and both has same behavior.

Code:

index.html

<script src="https://apis.google.com/js/platform.js" async defer></script>

<meta name="google-signin-client_id" content="my_client_id">

login.html

<div class="g-signin2" data-onsuccess="onSignIn"></div>  

login.js

angular.module('app').controller('LoginCtrl', function($scope) {
    window.onSignIn = function(googleUser) {
        // Get some info
    }
});
Emma
  • 8,518
  • 1
  • 18
  • 35
  • you need to give more details and maybe provide an example. When the button is not showing do you see any errors in the Console ? Is this happening in a production env or your local machine? Could it be that you have some network lag/issue and it's not grabbing the platform.js file ? – sirrocco Sep 28 '15 at 17:26
  • I am testing on localhost and no errors in console. When I see the button, I see "waiting for https://apis.google.com/js/platform.js...". However when I do not see the button, it does not show "waiting...." at the bottom left on Chrome. – Emma Sep 28 '15 at 19:35
  • I think the problem may lie with when the code is executed. For example, if the google library is cached, or otherwise executed before the angular library is loaded and your module is executed, the data-onsuccess handler will call the onSignIn method, which may not be attached to the window object yet. I would try, if possible, to move your onSignIn declaration outside of the angular code, so that it is available as soon as possible. – Neil S Sep 28 '15 at 21:00

2 Answers2

32

My guess is that the platform.js script waits for the DOM-ready event and then looks for your div with the "g-signin2"-class. Though, in Angularjs things work a little different. The reason that it works sometimes, is because sometimes your div has been rendered by Angular and sometimes is hasn't been rendered before the Dom-ready event. There's documentation on how to get the same result with your own javascript. I made an example that follows your routing.

<!doctype html>
<html lang="en" ng-app="app">
<head>
    <meta charset="UTF-8">
    <title>Document</title>
</head>
<body>
    <div ng-view></div>

<script src="https://apis.google.com/js/platform.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.6/angular.min.js"></script>
<script src="https://code.angularjs.org/1.4.6/angular-route.min.js"></script>
<script>
    angular.module('app',['ngRoute'])
            .config(['$routeProvider',function($routeProvider){
                $routeProvider
                        .when('/log-in', {
                            template: '<button ng-click="logInCtrl.onLogInButtonClick()">Log In</button>',
                            controller: 'LogInController',
                            controllerAs: 'logInCtrl'
                        }).otherwise({
                            redirectTo:'/log-in'
                        });
            }])
            .controller('LogInController',function(){
                var self = this; //to be able to reference to it in a callback, you could use $scope instead
                gapi.load('auth2', function() {//load in the auth2 api's, without it gapi.auth2 will be undefined
                    gapi.auth2.init(
                            {
                                client_id: 'CLIENT_ID.apps.googleusercontent.com'
                            }
                    );
                    var GoogleAuth  = gapi.auth2.getAuthInstance();//get's a GoogleAuth instance with your client-id, needs to be called after gapi.auth2.init
                    self.onLogInButtonClick=function(){//add a function to the controller so ng-click can bind to it
                        GoogleAuth.signIn().then(function(response){//request to sign in
                            console.log(response);
                        });
                    };
                });
            });
</script>
</body>
</html>

Or as a directive:

<!doctype html>
<html lang="en" ng-app="app" ng-controller="MainController">
<head>
    <meta charset="UTF-8">
    <title>Document</title>
</head>
<body>
    <google-sign-in-button on-sign-in="onSignIn(response)" g-client-id="CLIENTID.apps.googleusercontent.com"></google-sign-in-button>

<script src="https://apis.google.com/js/platform.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.6/angular.min.js"></script>
<script>
    angular.module('app',[])
            .controller('MainController',['$scope', function ($scope) {
                $scope.onSignIn=function(response){
                    console.log(response);
                }
            }])
            .directive('googleSignInButton',function(){
                return {
                    scope:{
                        gClientId:'@',
                        callback: '&onSignIn'
                    },
                    template: '<button ng-click="onSignInButtonClick()">Sign in</button>',
                    controller: ['$scope','$attrs',function($scope, $attrs){
                        gapi.load('auth2', function() {//load in the auth2 api's, without it gapi.auth2 will be undefined
                            gapi.auth2.init(
                                    {
                                        client_id: $attrs.gClientId
                                    }
                            );
                            var GoogleAuth  = gapi.auth2.getAuthInstance();//get's a GoogleAuth instance with your client-id, needs to be called after gapi.auth2.init
                            $scope.onSignInButtonClick=function(){//add a function to the controller so ng-click can bind to it
                                GoogleAuth.signIn().then(function(response){//request to sign in
                                    $scope.callback({response:response});
                                });
                            };
                        });
                    }]
                };
            });
</script>
</body>
</html>

After writing the previous examples I found a better and easier way to implement it. With this code you inherit the same button as you normally would.

<!doctype html>
<html lang="en" ng-app="app" ng-controller="MainController">

<head>
  <meta charset="UTF-8">
  <title>Document</title>
  <meta name="google-signin-client_id" content="CLIENTID.apps.googleusercontent.com">
</head>

<body>
  <google-sign-in-button button-id="uniqueid" options="options"></google-sign-in-button>

  <script src="https://apis.google.com/js/platform.js"></script>
  <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.6/angular.min.js"></script>
  <script>
    angular.module('app', [])
      .controller('MainController', ['$scope',
        function($scope) {
          //for more options visit https://developers.google.com/identity/sign-in/web/reference#gapisignin2renderwzxhzdk114idwzxhzdk115_wzxhzdk116optionswzxhzdk117
          $scope.options = {
            'onsuccess': function(response) {
              console.log(response);
            }
          }
        }
      ])
      .directive('googleSignInButton', function() {
        return {
          scope: {
            buttonId: '@',
            options: '&'
          },
          template: '<div></div>',
          link: function(scope, element, attrs) {
            var div = element.find('div')[0];
            div.id = attrs.buttonId;
            gapi.signin2.render(div.id, scope.options()); //render a google button, first argument is an id, second options
          }
        };
      });
  </script>
</body>

</html>
Swimburger
  • 6,681
  • 6
  • 36
  • 63
0

I found another way to solve the problem a liitle bit simply.

The explanation from @sniel is perfect but I will let you know more simple solution.

you can use below sample code very simiraliry with using $watch https://developers.google.com/identity/sign-in/web/build-button

<!-- html part -->
<div id="signInButton"></div>


//gapi is Asynchronously loaded so you need to watch
$scope.$watch('gapi', function(newValue,oldVale){
        if(newValue !== oldVale){
            if(gapi){
                gapi.signin2.render('signInButton',
                    {
                        'onsuccess': $scope.onSuccess,
                        'onfailure': $scope.onFailure,
                        'scope':'https://www.googleapis.com/auth/plus.login'
                    }
                );
            }
        }
    });
MinLoveSu
  • 243
  • 1
  • 2
  • 15