amgo85
September 20, 2022, 1:02pm
1
Hi everyone
I am in the chapter titled “Project: Authentication and Authorization (1h7m)”
I have been searching a lot before posting these two issues
displayName doesn’t show the name though I am logged in.
also login <li *ngIf=“!auth.user$”> directive is supposed to show up only when no user is logged in, but it always not shown, on the other hand <li ngbDropdown *ngIf=“auth.user$” class=“nav-item dropdown”> must only show when there is a user object, but it is always visible.
Did any one come across this issue??
Could anyone help please?
amgo85
September 20, 2022, 1:03pm
2
I am posting here the other screenshot for no user logged in
Can you share your Typescript where the auth.user$ variable is defined? Use a code fence like this:
```
your Typescript here
```
amgo85
September 20, 2022, 4:42pm
4
<li class="nav-item" *ngIf="!(auth.user$)">
<a class="nav-link" routerLink="/login">Login</a>
</li>
<!--
<ng-template #anonymousUser>
<li class="nav-item">
<a class="nav-link" routerLink="/login">Login</a>
</li>
</ng-template>
-->
<li ngbDropdown *ngIf="auth.user$" class="nav-item dropdown">
<a ngbDropdownToggle class="nav-link dropdown-toggle" data-toggle="dropdown" aria-expanded="false">{{ auth.user$.displayName }}</a>
<div ngbDropdownMenu class="dropdown-menu">
<a ngbDropdownItem class="dropdown-item" routerLink="/my/orders">My Orders</a>
<a ngbDropdownItem class="dropdown-item" routerLink="/admin/orders">Manage Orders</a>
<a ngbDropdownItem class="dropdown-item" routerLink="/admin/products">Manage Products</a>
<a ngbDropdownItem class="dropdown-item" (click)="auth.logout()">Log Out</a>
</div>
</li>
That looks like your HTML file. I want the contents of your Typescript file (it should end in .ts
).
amgo85
September 20, 2022, 5:24pm
6
Sorry I didn’t read .ts
the former was bs-navbar.component.html
here is typescript
import { Component, AfterViewInit } from '@angular/core';
import { AuthService } from '../auth.service';
@Component({
selector: 'bs-navbar',
templateUrl: './bs-navbar.component.html',
styleUrls: ['./bs-navbar.component.css']
})
export class BsNavbarComponent implements AfterViewInit {
constructor(protected auth: AuthService) {
const displayName = auth.user$.displayName;
console.log('username inside constructor is: ' + auth.user$.displayName);
}
ngAfterViewInit(): void {
console.log('username inside ngAfterViewInit is: ' + this.auth.user$.displayName);
}
logout(){
this.auth.logout();
}
}
Assuming AuthService is following the convention of naming the field user$ because it is an Observable, you probably have to pipe it to async in order to get the value out of the Observable:
*ngIf="auth.user$ | async"
Otherwise it will treat the non-null Observable as “true” even if the value in the Observable is null.
amgo85
September 20, 2022, 6:54pm
8
Actually this async pipe never worked with me, I googled that so much, but nothing worked.
I did the other option that mosh said, that is to unsubscribe from Observable.
here is auth.service.ts
import { Injectable, OnDestroy } from '@angular/core';
import { ActivatedRoute } from '@angular/router';
import { AngularFireAuth } from 'angularfire2/auth';
import * as firebase from 'firebase'
import { Observable, Subscription } from 'rxjs';
@Injectable({
providedIn: 'root'
})
export class AuthService implements OnDestroy{
user$: Observable <firebase.User> | any;
$subscription!: Subscription;
constructor(
private afAuth: AngularFireAuth,
private route: ActivatedRoute) {
this.user$ = afAuth.authState;
this.$subscription = afAuth.authState.subscribe(x => console.log(x));
}
login(){
let returnUrl = this.route.snapshot.queryParamMap.get('returnUrl') || '/';
localStorage.setItem('returnUrl', returnUrl);
this.afAuth.auth.signInWithRedirect(new firebase.auth.GoogleAuthProvider());
//console.log('username is: ' + this.afAuth.auth.currentUser?.displayName);
}
logout(){
this.afAuth.auth.signOut();
}
ngOnDestroy(): void {
this.$subscription.unsubscribe();
}
}
Assuming you are using the current version of AngularFire, they literally advise you to use async
with their user
field: https://github.com/angular/angularfire/blob/master/docs/auth/getting-started.md
<div *ngIf="auth.user | async as user; else showLogin">
Is there any particular reason you are using authState
rather than the documented user
field already present in the AngularFireAuth
object?
1 Like
FYI: I answered a similar version of this question a few months ago: firebase.User issue - #2 by jmrunkle
amgo85
September 21, 2022, 5:49am
11
This code I followed from course, but I will check it & get back
Thanks a lot Mr. Jason
amgo85
September 21, 2022, 7:57pm
12
Thank you Mr. Jason
This Github page was helpful,
I have another problem though, that is the admin guard.
I noticed that Mosh was using this library
import 'rxjs/add/operator/switchMap';
import 'rxjs/add/operator/map';
& I was receiving this error
./src/app/admin-auth-guard.service.ts:2:0-37 - Error: Module not found: Error: Package path ./add/operator/switchMap is not exported from package .\oshop\node_modules\rxjs (see exports field in .\oshop\node_modules\rxjs\package.json)
Then I found an advice in stackoverflow that I have to import this way
import { map, switchMap } from 'rxjs/operators';
The error message was gone, but I got runtime error as in the attached photo
& the non admin user, the two highlighted links were also visible
Yes, Mosh’s use of RxJS is a bit behind the current times. At this point you mostly use the operators like this using the pipe
method:
someObservable.pipe(map(value => ...))
Here are documentation links for map and switchMap . In particular, the examples at the end can be helpful.
If you are using the style that Mosh demonstrated, it will not work. You can paste the contents of your admin-auth-guard.service.ts file if you need help translating it.
amgo85
September 21, 2022, 8:24pm
14
import { Injectable } from '@angular/core';
import { CanActivate } from '@angular/router';
import { Observable } from 'rxjs';
import { AuthService } from './auth.service';
import { UserService } from './user.service';
import { AppUser } from './models/app-user';
//import { map, switchMap } from 'rxjs/operators';
//import 'rxjs/add/operator/switchMap';
//import 'rxjs/add/operator/map';
import * as firebase from 'firebase/';
//import * as firebase from 'firebase';
@Injectable({
providedIn: 'root'
})
export class AdminAuthGuard implements CanActivate {
constructor(private auth: AuthService, private userService: UserService) { }
canActivate(): Observable<boolean> {
return this.auth.appUser$
.map((appUser: AppUser) => appUser.isAdmin); // it is giving error
}
}
![admin-auth|690x380](upload://l76Sl3i0474HGvdtRvkbnYyFRDY.jpeg)
Try this:
return this.auth.appUser$
.pipe(map((appUser: AppUser) => appUser.isAdmin));
That’s how you use map
in current rxjs.
And you still have to import the operator.
amgo85
September 22, 2022, 9:11am
16
I tried it the error disappeared, but I got this runtime error
Could it be this reason: it is not an observable, but your function is asking for one
[Update]: I added this import statement
import { Observable, switchMap } from 'rxjs';
& the error message changed
What does your auth.service.ts file look like now?
amgo85
September 22, 2022, 7:26pm
18
import { Injectable } from '@angular/core';
import { ActivatedRoute } from '@angular/router';
import { AngularFireAuth } from 'angularfire2/auth';
import * as firebaseAlias from 'firebase/';
//import { GoogleAuthProvider } from 'firebase/auth'
//import { AngularFireAuth } from '@angular/fire/compat/auth'; // https://github.com/angular/angularfire/blob/master/docs/auth/getting-started.md
//import * as firebase from 'firebase';
//import firebase from 'firebase/compat/app'; // https://github.com/angular/angularfire/blob/master/docs/auth/getting-started.md
import { Observable, switchMap } from 'rxjs';
import { AppUser } from './models/app-user';
import { UserService } from './user.service';
@Injectable({
providedIn: 'root'
})
export class AuthService {
user$: Observable <firebaseAlias.User> | any;
constructor(
private userService: UserService,
private afAuth: AngularFireAuth,
private route: ActivatedRoute) {
this.user$ = afAuth.authState;
//this.user$ = afAuth.user; // from https://forum.codewithmosh.com/t/firebase-user-issue/12042/2
}
login(){
let returnUrl = this.route.snapshot.queryParamMap.get('returnUrl') || '/';
localStorage.setItem('returnUrl', returnUrl);
this.afAuth.auth.signInWithRedirect(new firebaseAlias.auth.GoogleAuthProvider());
//this.afAuth.auth.signInWithPopup(new firebaseAlias.auth.GoogleAuthProvider());
//console.log('username is: ' + this.afAuth.auth.currentUser?.displayName);
}
logout(){
this.afAuth.auth.signOut();
}
get appUser$(): Observable<AppUser> {
return this.user$
.rxjs.switchMap((user: firebase.User) => this.userService.get(user.uid))
}
}
So switchMap
works almost exactly the same way as map
. Try this:
get appUser$(): Observable<AppUser> {
return this.user$
.pipe(switchMap((user: firebase.User) => this.userService.get(user.uid)));
}
amgo85
September 23, 2022, 6:50pm
20
I tried it but getting this error