The header remains unchanged even after verifying the user's login status

Currently, I am using Angular 11 for the front-end and Express for the back-end. I am facing an issue with determining if a user is logged in so that I can display the correct header. Even after logging in and setting a cookie in the browser upon redirection to the home page, the header does not update. Similarly, when clicking the logout button, although the cookie is removed, the header remains unchanged. I have tried debugging using Chrome Dev Tools and checking the value of the isLogged$ variable, but it keeps showing as undefined. I suspect there might be an error with the BehaviourSubject.

I am hoping someone can assist me in identifying my mistake, especially since I am relatively new to Angular.

I will provide some files for better context.

auth.service.ts

import { HttpClient } from '@angular/common/http';
import { Injectable } from '@angular/core';
import { BehaviorSubject } from 'rxjs';
import { Observable } from 'rxjs/internal/Observable';
import { catchError, tap, map } from 'rxjs/operators';
import { IUser } from '../shared/interfaces';

@Injectable()
export class AuthService {

  currentUser: IUser | null;

  private _currentUser: BehaviorSubject<IUser | null> = new BehaviorSubject(undefined);
  currentUser$ = this._currentUser.asObservable();
  isLogged$ = this.currentUser$.pipe(map(user => !!user));

  constructor(private http: HttpClient) { }

  login(data: any): Observable<any> {
    return this.http.post(`/user/login`, data).pipe(
      tap((user: IUser) =>
        this._currentUser.next(user))
    );
  }

  signup(data: any): Observable<any> {
    return this.http.post(`/user/signup`, data).pipe(
      tap((user: IUser) => this._currentUser.next(user))
    );
  }

  logout(): Observable<any> {
    return this.http.post(`/user/logout`, {}).pipe(
      tap((user: IUser) => this._currentUser.next(null))
    );
  }

  authenticate(): Observable<any> {
    return this.http.get(`/user/verify`).pipe(
      tap((user: IUser) => this._currentUser.next(user)),
      catchError(() => {
        this._currentUser.next(null);
        return [null];
      })
    );
  }
}

navigation.component.ts

import { Component, OnInit } from '@angular/core';
import { Router } from '@angular/router';
import { AuthService } from '../auth.service';

@Component({
  selector: 'app-navigation',
  templateUrl: './navigation.component.html',
  styleUrls: ['./navigation.component.css']
})
export class NavigationComponent implements OnInit {

  isLogged$ = this.authService.isLogged$;

  constructor(
    private authService: AuthService,
    private router: Router) { }

  ngOnInit(): void {
  }
  
  logoutHandler(): void {
    this.authService.logout().subscribe(() => this.router.navigate(['/']));
  }
}

navigation.component.html

<nav class="navigation-wrapper">
    <article>
        <ul>
            <li class="home">
                <a href="/">Home</a>
            </li>
            <li *ngIf="!isLogged$" class="login">
                <a href="/login">Login</a>
            </li>
            <li *ngIf="!isLogged$" class="sign-up">
                <a href="/signup">Sign Up</a>
            </li>
            <li *ngIf="isLogged$" class="my-cities">
                <a href="/my-cities">My Cities</a>
            </li>
            <li *ngIf="isLogged$" class="account">
                <a href="/account">Account</a>
            </li>
            <li *ngIf="isLogged$" class="logout">
                <a (click)="logoutHandler()">Logout</a>
            </li>
        </ul>
    </article>
</nav>

auth.interceptor.ts

import { Injectable, Provider } from '@angular/core';
import { HttpEvent, HttpHandler, HttpInterceptor, HttpRequest, HTTP_INTERCEPTORS } from '@angular/common/http';
import { Observable } from 'rxjs';

import { environment } from '../../environments/environment';

@Injectable()
export class AuthInterceptor implements HttpInterceptor {
    apiUrl = environment.apiURL;

    intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
        req = req.clone({
            url: `${this.apiUrl}${req.url}`,
            withCredentials: true
        });
        return next.handle(req);
    }
}

export const authInterceptorProvider: Provider = {
    provide: HTTP_INTERCEPTORS,
    useClass: AuthInterceptor,
    multi: true
};

user.js (back-end controller)

const User = require('../models/User');

const utils = require('../utils');
const authCookieName = process.env.authCookieName;

module.exports = {
    get: async (req, res, next) => {
        try {
            const { id } = req.params;

            const user = await User.findById(id).populate('followedCities').lean();

            res.send(user);
        } catch (e) {
            console.error(e);
            next();
        }
    },

    post: {
        signup: async (req, res, next) => {
            const { name, email, password } = req.body;
            console.log(req.body);
            console.log(name, email, password);
            const hashedPassword = await utils.auth.hashPassword(password);

            try {
                const newUser = await User.create({
                    name,
                    email,
                    password: hashedPassword
                });

                const user = await User.findOne({ email }).lean();
                const token = utils.jwt.createToken({ id: user._id });

                res.cookie(authCookieName, token).send(user);
            } catch (e) {
                console.error(e);
                next();
            }
        },

        login: async (req, res, next) => {
            const { email, password } = req.body;
            console.log(req.body);

            try {
                const status = await utils.auth.checkPassword(email, password);

                if (!status) {
                    res.status(401).send('Invalid username or password!');
                }

                const user = await User.findOne({ email }).lean();
                const token = utils.jwt.createToken({ id: user._id });

                res.cookie(authCookieName, token).send(user);
            } catch (e) {
                console.error(e);
                next();
            }
        },

        verifyLogin: (req, res, next) => {
            console.log(req.headers);
            const token = req.cookie.authCookieName || '';

            Promise.all([
                utils.jwt.verifyToken(token)
            ])
                .then(([data]) => {

                    User.findById(data.id)
                        .then((user) => {
                            return res.send({
                                status: true,
                                user
                            })
                        });
                })
                .catch(err => {
                    if (['token expired', 'jwt must be provided'].includes(err.message)) {
                        res.status(401).send('UNAUTHORIZED!');
                        return;
                    }

                    res.send({
                        status: false
                    })

                    next();
                })
        },

        logout: async (req, res, next) => {
            try {
                res.clearCookie(authCookieName).send('Logout Successful!');
            } catch (e) {
                console.error(e);
                next();
            }
        }
    }
};

Answer №1

isLogged$ is an Observable.

You cannot simply write in navigation.component.html

<li *ngIf="!isLogged$" class="login">

Instead, try using

<li *ngIf="!(isLogged$ | async)" class="login">

The async pipe subscribes to either an Observable or a Promise and returns the most recent value it has received.

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Setting up Node on a Ubuntu system

Currently, I am in the process of installing Node.js to run my Angular2 program. However, I encountered an error during the installation: npm WARN npm npm does not support Node.js v0.10.25 npm WARN npm You should probably upgrade to a newer version of nod ...

Having trouble with the sendFile function in your Node app built with Express?

Within my server.js file (where 'app' is an instance of express), I have the following code snippet. It is meant to send back a file named index.html which resides in the same directory as the server.js file. app.get('/', function (req ...

Utilize the datasource.filter method within an *ngFor loop

I have a table that filters based on certain search criteria, which I implemented using the example found at: https://material.angular.io/components/table/examples Now, I am wondering if it is possible to apply this filtering functionality to an *ngFor lo ...

My goal is to design a dynamic form consisting of various components, ensuring that all required fields are validated upon the user's submission

I am trying to set up an Angular reactive form with multiple sub-components. My goal is to validate both the parent and child form components at once when the user clicks the Submit button. Currently, I am running into an issue where error messages only ...

Nodejs Express route malfunctioning

My current situation is quite straightforward. I am following a tutorial by Max, but I seem to be facing an issue. When I access http://localhost:3000/message, it always returns the index page instead of the desired route. Specifically, only the initial ...

Navigating Angular on Internet ExplorerUnderstanding Angular's Compatibility

Having some trouble with an Angular app I'm developing. It displays perfectly on Chrome, but not at all on IE. Any tips on how to configure IE to show it correctly, or should I make changes to the app to ensure compatibility with IE? You can see how i ...

Exploring Angular 4: Iterating Over Observables to Fetch Data into a Fresh Array

Context Currently, I am in the process of developing a find feature for a chat application. In this setup, each set of messages is identified by an index. The goal of the `find()` function is to retrieve each message collection reference from the `message ...

TS-2304 Error - 'Iterable' not found in TypeScript when trying to import 'jquery' into a '.ts' file

Currently, I am utilizing TypeScript version 2.4 in Visual Studio Code for development. My approach involved installing jQuery via NPM using the given command: npm install --save @types/jquery Subsequently, I obtained the source code for the jquery modul ...

Make sure to wait for the fetch response before proceeding with the for loop in JavaScript using Node.js

I am facing an issue with my function that connects to a SOAP web service. The problem arises from the fact that the web service has limited connections available. When I use a for or foreach loop to search through an array of items in the web service, aro ...

Uncover hidden mysteries within the object

I have a function that takes user input, but the argument type it receives is unknown. I need to make sure that... value is an object value contains a key named "a" function x(value: unknown){ if(value === null || typeof value !== 'obj ...

Unable to utilize the post method in Node.js

<form class="ui form" action"/submit" method="post"> <div class="field"> <label>Time dedicated to studying:</label> <input type="number" name="study" placeholder="Total time spent on studies:"> ...

Pause code execution and prompt user interaction within a loop - React

I have been working on adding an "add all" button to my React app. To achieve this, I am passing a function to the onClick method of the button: for (element in elements) { await uploadfunction(element) } const uploadfunction = async (element) => ...

I am experiencing issues with my HTML select list not functioning properly when utilizing a POST service

When using Angularjs to automatically populate a list with *ngFor and then implementing a POST service, the list stops functioning properly and only displays the default option. <select id="descripSel" (change)="selectDescrip()" > <option >S ...

Receiving encoded characters in the response

URL: I have encountered an issue where I am trying to retrieve the PDF file from the URL above using code. In tools like Postman or Insomnia, I am able to see the output as expected in PDF format. However, when I attempt it with code, I am receiving rando ...

It seems like there is an issue with the res.render function as it is

My goal is to capture an input from my main website. Once the input is submitted, the page should automatically redirect to /view. Despite successfully triggering the redirection with console.log(), the res.render function does not seem to be working as ex ...

Angular 5 - Implementing Horizontal Scroll with Clickable Arrows

In my Angular application, I have successfully implemented horizontal scrolling of cards. Now, I am looking to enhance the user experience by adding "Left" and "Right" buttons that allow users to scroll in either direction within their specific msgCardDeck ...

What is the best way to fake the retrieval of '$location' using mock for angular.element(document).injector() in jasmine 3.6?

Currently, I am encountering an issue. I am unsure of how to mock the following method: private angularLocation = angular.element(document).injector().get('$location'); In my hybrid angular application, this method is crucial for navigating betw ...

Event when a panel menu in PrimeNG is clicked

<p-panelMenu [model]="items" [style]="{'width':'300px'}" (click)="clicked($event)"></p-panelMenu>`<p-dialog header="Title" [(visible)]="display"> page 1 ` this is my ts ` click: any; display: boolean = false; ...

Unable to locate the specified nested module during the import process

Imagine a scenario where we have two packages, namely package1 and package2. When package2 attempts to import the module from package1, an error is thrown stating that the module is not found. The import statement in question looks like this: import { ... ...

Is it possible for me to transfer a change to the worldwide namespace from my library?

I am looking to enhance an existing TypeScript type within the global namespace in my library and then expose it for use in other projects. Can this be done? Below is my current code: Promise.ts Promise.prototype.catchExtension = function<T>(this ...