displayName & ngIf not working properly

Hi everyone
I am in the chapter titled “Project: Authentication and Authorization (1h7m)”
I have been searching a lot before posting these two issues

  1. displayName doesn’t show the name though I am logged in.
  2. 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?

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
```
        <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).

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.

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

This code I followed from course, but I will check it & get back

Thanks a lot Mr. Jason

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.

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.

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?

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)));
    
  }

I tried it but getting this error