Open MaklaCof opened 7 years ago
Hi @MaklaCof, Thanks for the detailed reporting. It's an error that you can safely ignore, however I understand it is not the ideal behavior. The widget is not meant to be removed from the page after a sign in or sign out, but many Single Page Apps will do that.
An alternative I would propose is to use this: https://developers.google.com/identity/sign-in/web/build-button#building_a_button_with_a_custom_graphic so you can control more precisely the lifecycle of the button. It will also help you automatically sign in the user if they were previously signed in:
auth2 = gapi.auth2.init({...}).then(auth => {
if (auth.isSignedIn.get()) {
// User already signed in, no need for the button, directly route to the "signed-in" state.
}
});
Does that help?
I will check it, but I can not ignore it. On successful login I do some work (some request are made to server, meni are reordered, some are hidden some are showed), but error in JavaScript stop executing this things.
Unfortunately it is not working. It raise the same error. This is my updated code:
ngAfterViewInit()
{
gapi.load("auth2", () =>
{
const auth = gapi.auth2.init({
client_id: "**********"
});
if (auth.isSignedIn.get())
{
this.onGoogleLoginSuccess(auth.currentUser.get());
}
else
{
auth.attachClickHandler(this.el.nativeElement, {}, (user) =>
{
this.onGoogleLoginSuccess(user);
},
(error) =>
{
this.onGoogleLoginError(error);
});
}
/*gapi.signin2.render(
this.googleLoginButtonId,
{
onsuccess: (user) => { this.onGoogleLoginSuccess(user); },
onfailure: (error) => { this.onGoogleLoginError(error); },
scope: this.scopes.join(" ")
});*/
});
}
onGoogleLoginSuccess(user: gapi.auth2.GoogleUser)
{
this.isGoogleAccountVisible = true;
const profile = user.getBasicProfile();
this.googleAccount = profile.getEmail();
const auth = user.getAuthResponse(true);
this.authService.loginWithGoogle(new GoogleLoginModel(auth.access_token, profile.getName()))
.subscribe((profile) => { this.getData(); });
}
onGoogleLoginError(error: string)
{
this.error = { code: eResponseErrorCodes.Error, message: error };
}
loginClick()
{
this.isGoogleAccountVisible = true;
this.authService.login(new LoginUser(this.loginForm.value.email, this.loginForm.value.password))
.subscribe((profile) =>
{
this.getData();
},
(err) =>
{
this.isGoogleAccountVisible = false;
this.error = err;
});
}
private async getData()
{
delete this.error;
await this.usersService.readAndSave();
this.isGoogleAccountVisible = false;
delete this.googleAccount;
this.router.navigate([""]);
}
If I return to original problem:
The widget is not meant to be removed from the page after a sign in or sign out, but many Single Page Apps will do that.
I think you should really fix this. It is a little funny that Google (Angular2) is not working with himself (SignIn). It is not that hard to change this code:
window.document.getElementById((c?"not_signed_in":"connected")+a.El).style.display="none";
to this:
var t = window.document.getElementById((c?"not_signed_in":"connected")+a.El);
if (t) t.style.display="none";
Hi @MaklaCof, Thanks for trying to implement what I recommended.
Are you saying this is still raising the same Uncaught TypeError: Cannot read property 'style' of null
? I'm surprised as the method used now should not depend at all the same library gapi.signin2
. Have you removed the HTML for the previous button? If there is still a button with the same class g-signin2
it will initialize as before.
I also see that we could avoid this error raised very easily, however, that does not fix the real issue here, being that the button/library state is not properly cleaned up by removing the button from the DOM. A more programmatic approach should be used within Angular framework instead of using the button widget.
Could you please make sure that gapi.signin2
is not initialized somehow in your code? On my end I will look at how properly fix the issue within the library itself.
Thanks!
Hi. Thank you. I tried again and it works. I must have forget something in previous try. Not sure what.
Glad it worked!
I'm getting the same error on ReactJs.
The first time the page is loaded everything is Ok. If I change from one page to another, the element is removed from the Dom, and when I go to the page with the button, gapi.signin2.render is called again.
But it seems that the events from the first call still alive, and that events show the error of "null". If I repeat the process, the events still there, but this time twice.
It feels that what we're missing is a function to clean all events when the DOM is removed.
Hi @JordiOrriols, as I mentioned earlier, gapi,signin2
is unfortunately not designed to be used in a single page application, hence the issue you observe. For now, you can build a custom button which will help you control the lifecycle more precisely: https://developers.google.com/identity/sign-in/web/build-button#building_a_button_with_a_custom_graphic.
We could potentially implement a dispose()
method that would dispose properly of all the listeners, method that you would call when the button is removed from DOM. Would that help?
This would help for sure. It would be great to have this method on the future! Thank you!
If you search gapi Cannot read property 'style' of null
in Google search, you will see how much pain we are suffering. Yes we can implement it ourself to fix the issue but it would be much better if Google can make it works with SPA
@tom10271 I apologize for the inconvenience. We are going to roll out a quick temporary fix while we work on a longer term solution for Single Page Application. I'll update this thread when it's deployed.
@tom10271 @JordiOrriols The error should not be raised anymore now. Please let me know if that works for you!
Any update on this? This bug make the google sign in button disappear completely.
@lapat what do you mean? In this bug's context, the framework used already remove the button from the DOM.
@TMSCH maybe I'm experiencing a different bug? Google sign in button will randomly not show up on my site with this error:
b=gapi.loaded2:20 Uncaught TypeError: Cannot read property 'style' of undefined at QG (https://apis.google.com//scs/apps-static/_/js/k=oz.gapi.en.8ggGBlnRYmw.O/m=signin2/exm=auth2,client/rt=j/sv=1/d=1/ed=1/am=AQ/rs=AGLTcCORPt1ErXTGopWn2el2oYFxRcgjyw/cb=gapi.loaded2:20:434) at H.S (https://apis.google.com/_/scs/apps-static/_/js/k=oz.gapi.en.8ggGBlnRYmw.O/m=signin2/exm=auth2,client/rt=j/sv=1/d=1/ed=1/am=AQ/rs=AGLTcCORPt1ErXTGopWn2el2oYFxRcgjyw/cb=gapi.loaded_2:22:72) at tW (https://apis.google.com/_/scs/apps-static/_/js/k=oz.gapi.en.8ggGBlnRYmw.O/m=signin2/exm=auth2,client/rt=j/sv=1/d=1/ed=1/am=AQ/rs=AGLTcCORPt1ErXTGopWn2el2oYFxRcgjyw/cb=gapi.loaded_2:23:32) at https://apis.google.com/js/platform.js?onload=onLoadCallback:50:615 at https://apis.google.com/js/platform.js?onload=onLoadCallback:20:348 at hb (https://apis.google.com/js/platform.js?onload=onLoadCallback:12:467) at b (https://apis.google.com/js/platform.js?onload=onLoadCallback:20:331) at G.(anonymous function).G.(anonymous function) (https://apis.google.com/js/platform.js?onload=onLoadCallback:20:421) at c (https://apis.google.com/js/platform.js?onload=onLoadCallback:20:388) at https://apis.google.com/js/platform.js?onload=onLoadCallback:20:352
Hmm this error should not happen anymore. Are you caching api.js or platform.js?
@TMSCH, should I be caching api.js or platform.js? If so, how do I that? Thanks man! This bug is killing me.
You should not, no. I am just surprised that you're getting this error. I tried removing the button from DOM and then signing out, which should update its state, and it doesn't raise the error.
Could you explain in a bit more details what you mean by "Google sign in button will randomly not show up on my site with this error:"? How does that happen? Could you provide a minimal snippet of code that reproduces the issue?
@TMSCH thanks for your help man!
The code is copy and pasted from here: Google sign in
I use it on my site: coinflash
It appears that the error appears sometimes when a user signs off my site and then tries to sign back in or a user is directed to a page in my site and the 'sign-off' method is called even though the user is not signed in. Although, even these steps will not reproduce the error consistently.
@lapat did this error happen recently? i.e. after I pushed the fix 10 days ago?
I am not able to reproduce it in your app.
@TMSCH it's very hard to reproduce. I saw it happened yesterday on my friends Chrome on a Mac. Maybe some browsers were caching the script? I just added a random string to the end of the URL now to force it to always get latest JS file from google server: <script src="https://apis.google.com/js/api:client.js?dev=" + Math.floor(Math.random() * 1000)>
You shouldn't have to add the random string, this endpoint doesn't set any cache headers.
If I don't have a way to reproduce it, I can't debug :(
@TMSCH okay let me try to reproduce and I'll hit you back up. Thanks for being responsive and cool!
You're welcome :)
Hi @TMSCH so this bug is still showing up for my users. I know you can't reproduce it but what would you recommend would be the way to fix it? I don't think building a custom google sign in button would do it because it uses the same JS files, right? Building a custom Google Sign-In button
Or maybe build my own HTML button that acts just like a Google sign in button?
I did just that:
<div *ngIf="!loginInProgress">
<div *ngIf="googleAuth" class="button2" (click)="googleLoginHandler()">
<span>
<img src="https://google-developers.appspot.com/identity/sign-in/g-normal.png" />
</span>
Prijava z Google računom
</div>
</div>
and code:
public googleAuth: gapi.auth2.GoogleAuth;
ngAfterViewInit()
{
this.initGapi();
}
private initGapi()
{
if ((<any>window).gapi) //just if(gapi) is not enough. I still get error: gapi is not defined. If this will not fix error, another options is to remove async deffer when loading platform.js library.
{
gapi.load("auth2", () =>
{
this.zone.run(() =>
{
this.googleAuth = gapi.auth2.init({
client_id: OpPISTransferedData.googleClientId
});
if (this.googleAuth.isSignedIn.get())
{
this.onGoogleLoginSuccess(this.googleAuth.currentUser.get());
}
else
{
}
});
});
}
else
window.setTimeout(() => { this.initGapi(); }, 100);
}
public googleLoginHandler()
{
this.googleAuth.signIn()
.then(user => this.onGoogleLoginSuccess(user), error => this.onGoogleLoginError(error));
}
Hope it helps someone. There is just one problem. I am using Chrome, and reopen old tabs (settings). When I turn of my computer, and turn it on again and start Chrome, all my tabs are reopen, but sign in does not work (until I refresh the page). IT writes "window was closed by user".
@MaklaCof this is great, too bad about the 'window was closed by user' thing. Did you have a disappearing button before you replaced your code with this?
Yes I need to prepare scenario and report a bug, but didn't find the time to do it. I am not sure what you mean with disappearing button. I was having a problem with error in console, which is why I started the thread.
@MaklaCof thanks for providing a snippet to address the bug mentioned by @lapat. What do you mean by "IT write window was closed by user"? This is not an error coming from gapi.auth2
(popup_closed_by_user
is).
@lapat, the option you propose (building your custom button) would work as it doesn't use gapi.signin2
which has the issue. Any chance you could provide a link to reproduce the issue?
so this just happened to me and my button has dissappeared I am using this html
<div align="center" class="g-signin2" data-onsuccess="onSignIn" data-theme="dark">
then I have a javascript function onSignIn this was working until today.
Problem is still active, but it is not as easy as @MaklaCof suggests. In my case problem appears during navigation through tabs. And apparently element ID is not there after ngAfterViewInit indeed. So, I just added waiting for the button to appear, like this:
var btn = document.getElementById(btnId);
if (!btn) {
console.log('sso', 'wait for button started');
var waitForRes;
var waitFor = new Promise((res, rej) => {
waitForRes = res;
});
this.ngZone.runOutsideAngular(()=>{
var timer = setInterval(() => {
btn = document.getElementById(btnId);
console.log('sso', 'wait for button...', btn);
if (btn != null) {
clearInterval(timer);
this.ngZone.run(() => {
waitForRes();
});
}
}, 100);
});
await waitFor;
console.log('sso', 'button is there!', btn);
}
console.log('sso', 'renderGoogle Loaded');
gapi.signin2.render(btnId, { ...
So, there is nothing bad with gapi I believe... except that it was advised to use ngAfterViewInit for render call. But during SPA navigation with a router, switching components back and forth the target element ID that we supposed to path to Render call is actually not ready.
Anyone have any idea how to implement something similar in React? There's a similar issue, and what I understand is that it is happening because React removes a certain element from DOM? but that element needs to stay in DOM? My problem is more concerned with the recaptcha in firebase Auth with Phone.
If it's any help, I see this error periodically. Here's an example stack trace:
TypeError: Cannot read property 'style' of null
at Array.<anonymous> (https://www.gstatic.com/recaptcha/releases/lz3oilnU_FqeO0ydN3CXU9RT/recaptcha__en.js:225:269)
at UK.<anonymous> (https://www.gstatic.com/recaptcha/releases/lz3oilnU_FqeO0ydN3CXU9RT/recaptcha__en.js:83:305)
Haven't been able to reproduce locally yet, but I'll update if I do.
First, I already try to find solution on SO, but I think this is a bug.
I am using Angular2 application. (For backend I use ASP.NET core 1.1.) I also create SignIn button following official tutorial.
On successfully log in I re-route user to home page and then I get error. I get the same error if I logout from google account (in chrome).
The error is:
and line with error is:
window.document.getElementById((c?"not_signed_in":"connected")+a.El).style.display="none";
Of course getElementById returns null, because I am no longer on Login page, and Angular remove html from DOM. Same happens if I log out from Google account. The user could be anywhere.I read somewhere on SO, where tip for same error was
, but this is not possible here, since Angular2 remove element from DOM.
I think you should check first if element exists and then set style to whatever.