dimlucas dimlucas - 12 days ago 7
TypeScript Question

View not updating after change in Google API callback

I'm trying to implement a service that will use the Google API to log the user in and authorize the Sheets API. A simplified version of that service:

import {Injectable} from '@angular/core';

declare var gapi;

@Injectable()
export class MyService{
isLoggedIn: boolean = false;
clientId: string = "*******.apps.googleusercontent.com";
scopes: string[] = ["https://www.googleapis.com/auth/spreadsheets", "https://www.googleapis.com/auth/drive"];


constructor(){
gapi.load('client', () => {
console.log("Client Loaded");
gapi.auth.authorize({
client_id: this.clientId,
scope: this.scopes.join(' '),
immediate: true
}, (authResponse) => {
if(authResponse && !authResponse.error){
this.isLoggedIn = true;
console.log("User logged in, " + this.isLoggedIn);
}
else{
this.isLoggedIn = false;
console.log("User not logged in");
console.log(this.isLoggedIn);
}
});
});
}
}


I'm injecting that service to my AppComponent as a
private myService: MyService
. In my
app.component.html
I have a simple button that should be visible if the user has not authorized the API:

<button *ngIf="!myService.isLoggedIn">Authorize</button>


So what I want is:
- The service calls
gapi.auth.authorize

- If the user has authorized the API, then
isLoggedIn
should be set to
true
and the button should vanish
- If immediate authorization fails, the button should be visible so the user can trigger the authorization manually.

The issue I'm facing is that when immediate authorization succeeds and "User logged in, true" is printed in the console window, the button stays visible. The
*ngIf
bind does not work. The change in
isLoggedIn
is not reflected. I found a similar post here in SO but I had already suspected that it may have to do with the callbacks so I had already tried changing the value from a
setTimeout
within a
setTimeout
within another
setTimeout
and the view successfully updates.

I finally found a solution by injecting
ChangeDetectorRef
as
private detector
and calling
detector.detectChanges()
after setting a value for
isLoggedIn
.

So I'm not asking for a solution, what I'm asking is if someone knows why this weird behavior happens and why do I have to use the change detector for an operation that, to me at least, seems like something that should be handled by Angular itself. Or am I missing something and this behavior is normal?

Answer

gapi is not covered by Zone.js. You need to invoke change detection explicitly:

constructor(private cdRef:ChangeDetectorRef){
    gapi.load('client', () => {
        console.log("Client Loaded");
        gapi.auth.authorize({
            client_id: this.clientId,
            scope: this.scopes.join(' '),
            immediate: true
        }, (authResponse) => {
            if(authResponse && !authResponse.error){
                this.isLoggedIn = true;
                console.log("User logged in, " + this.isLoggedIn);
            }
            else{
                this.isLoggedIn = false;
                console.log("User not logged in");
                console.log(this.isLoggedIn);
            }
            cdRef.detectChanges(); // <<<< added
        });
    });
}