I'm implementing an Angular application based upon the google drive API.
My configuration is the following :
- "@angular/core": "~13.0.0",
- "@angular/forms": "~13.0.0",
- "@angular/material": "^13.0.1",
- "@types/gapi": "^0.0.41",
- "rxjs": "~7.4.0"
In short, there is a classical architecture with a service in charge of retrieving information from google API and a component in charge of displaying information.
Here are the code for the two elements :
First the service
import {Injectable} from "@angular/core";
import { Observable, Subject, BehaviorSubject } from 'rxjs';
declare const gapi: any;
@Injectable()
export class UserService {
public auth2: any;
public isSignIn: boolean;
private isSignInChange: BehaviorSubject<boolean> = new BehaviorSubject<boolean>(false);
public isSignInObs: Observable<boolean> = this.isSignInChange.asObservable();
constructor() {
//Init various variables
this.isSignIn = false;
this.isSignInChange.next(false);
//Load the google api for future OAUTH load
gapi.load('client:auth2', () => {this.googleInitClient(); });
}
private googleInitClient() {
gapi.client.init({
...
}).then( (res: any) => {
// Listen for sign-in state changes.
gapi.auth2.getAuthInstance().isSignedIn.listen( (res: boolean) => {this.updateSigninStatus(res)} );
// Handle the initial sign-in state.
this.updateSigninStatus(gapi.auth2.getAuthInstance().isSignedIn.get());
}, (error: any) => {
console.warn(error);
});
}
public updateSigninStatus(isSignedIn: boolean) {
this.isSignIn = isSignedIn;
this.isSignInChange.next(isSignedIn);
}
public signIn() {
gapi.auth2.getAuthInstance().signIn();
}
}
The component script
import { Component, OnInit } from '@angular/core';
import { Observable } from 'rxjs';
import { UserService } from '../_services/user.service';
@Component({
selector: 'app-my',
templateUrl: './my.component.html',
styleUrls: ['./my.component.scss']
})
export class MyComponent implements OnInit {
constructor() {
}
ngOnInit(): void {
}
public signIn() {
this.userService.signIn();
}
public signOut() {
this.userService.signOut();
}
}
The component template
<button *ngIf="userService.isSignInObs | async; else elseBlock" mat-button [matMenuTriggerFor]="my"><mat-icon>avatar</mat-icon></button>
<mat-menu #my="matMenu" yPosition="above" [overlapTrigger]="false">
<button mat-menu-item disabled><mat-icon>account_circle</mat-icon> {{currentUser?.nom}}</button>
<button mat-menu-item disabled><mat-icon>email</mat-icon> {{currentUser?.mail}}</button>
<button mat-menu-item routerLink="/configuration"><mat-icon>settings</mat-icon> Configuration</button>
<button mat-menu-item (click)="signOut()"><mat-icon>logout</mat-icon> Déconnexion</button>
</mat-menu>
<ng-template #elseBlock><a mat-button (click)="signIn()" title="Se connecter"><mat-icon>login</mat-icon></a></ng-template>
The problem
I have a common problem (many threads on the subject) but I can't get it to work properly. In fact, as soon as I finish loading the google drive API (this.isSignInChange.next(isSignedIn);), I should have the condition in my ngIf (userService.isSignInObs | async) changed to trueand my template should update. But whatever the implementing solution (I've try a lot of solution), the template does'tn change until I click some where in the view.
The only solution that I manage is to use ChangeDetectorRef but :
- I don't understand why I need to use it while it should work without it (ex: https://blog.angular-university.io/angular-reactive-templates/)
- If, despite everything, I decide to use it, it disturbs the functioning of the
<mat-expansion-panel>material component`
Can you help me to find what's wrong with my code !? Yhanks in advance
<mat-expansion-panel>. That means I can useChangeDetectorRefbut I don't like the idea... and appreciate any further help !