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

Is there a way to access the state value within the reducer function of createSlice?

Currently, I am utilizing redux-toolkit within my react project. A concern arises in a specific reducer inside the createSlice method where I aim to incorporate an existing array of entities from the state and then merge it with a new array before finalizi ...

Is it possible to exclude a portion of the code from running on Heroku staging environment?

Currently, I am looking into testing the functionality of my application by deploying it to the staging environment on Heroku. One specific feature involves saving data to s3, which I only want the application to run when in production mode and skip over ...

Ways to insert a raw query into a Sequelize model for an unmapped field

In my sports center model, I have the following code: import Sequelize from 'sequelize'; export default (sequelize, DataTypes) => { const Sportcenter = sequelize.define('Sportcenter', { id: { autoIncrement: t ...

Intermittent issue with Angular 2 encountered while following the Hero Editor tutorial on angular.io

I am encountering an occasional error in the console while following the angular.io tutorial using Mozilla Firefox. The error does not seem to impact the functionality or rendering of my application, and it only happens sporadically. If you could provide ...

Tips for implementing absolute import paths in a library project

In my workspace, I have a library with two projects: one for the library itself and another for a test application. ├── projects    ├── midi-app    └── midi-lib Within the workspace's tsconfig.json file, I set up paths for @a ...

Retrieve information prior to CanActivation being invoked

As I develop a web application utilizing REST to retrieve data (using Spring Boot), the server employs cookies for authenticating logged-in users. Upon user signing in, I store their information in the AuthenticationHolderService service (located in root ...

How can I integrate React-Router Link as a component within Material-UI using Typescript?

Hey everyone, I've encountered an issue while trying to utilize Material UI's component prop to replace the base HTML element of a component. Error: The error message reads: Type 'Props' is not generic. This problem arises from the fo ...

Tips for transferring a column in an array to an object field within an array

I have a piece of code where I need to pass values from the 'dataList' array into this.data object's 'labels' and 'datasets'-> data. When I try to directly set the values, I get an undefined result. So I created a variab ...

What steps are involved in setting up a SAML service provider using @node-saml/node-saml?

I'm currently working on a SAML SP implementation using the @node-saml/node-saml library. Despite creating the necessary source code, I am facing an issue with the SAML authentication not functioning as expected. Can anyone provide guidance on how to ...

Dealing with the endless looping problem in Next.js caused by useEffect

Looking to implement a preloader that spins while the content is loading and disappears once the loading is complete. However, encountering an issue where the site gets stuck on the loading page and keeps loading infinitely. I've tried multiple soluti ...

How to remove the border of the MUI Select Component in React JS after it has been clicked on

I am struggling to find the proper CSS code to remove the blue border from Select in MUI after clicking on it. Even though I managed to remove the default border, the blue one still persists as shown in this sandbox example (https://codesandbox.io/s/autumn ...

Using kotlinx.serialization to deserialize a JSON array into a sealed class

Stored as nested JSON arrays, my data is in rich text format. The plaintext of the string and annotations describing the formatting are stored in text tokens. At decode time, I aim to map the specific structure of these nested JSON arrays to a rich Kotlin ...

How can I retrieve distance data from the Google Maps API within an Angular service?

I am currently working on implementing the JavaScript version of the Google Maps API in order to resolve a CORS issue that I encountered with the regular API. Specifically, I am looking to calculate the distance and time between two destinations. How can I ...

Is there anyone available who can assist me in removing records from my Sequelize database?

I attempted to implement the code snippet below after coming across it on a popular platform, but for some reason, it doesn't seem to be functioning as expected. Posts.delete({ where: { createdAt: { isAfter: "2016-09-11" } } }) My goal is to remove ...

Deleting ion-radio from ion-select component in Ionic v4

Is there a way to remove ion-radio elements generated when calling ion-select with a popover interface? <ion-item> <ion-label>Popover</ion-label> <ion-select interface="popover" placeholder="Select One"> <ion-selec ...

Choosing an SVG Circle Using TypeScript

I am facing an issue with selecting a simple SVG <circle> element in my DOM using TypeScript: <svg viewBox="0 0 200 200"> <circle cx="50" cy="50" r="45" id="myCircle"/> </svg> In ...

An error occurred in the ngrx store with Angular during production build: TypeError - Unable to access property 'release' of undefined

After deploying my application and running it, I encountered an issue that seems to be happening only during production build at runtime. At this point, I am uncertain whether this is a bug or if there is a mistake in my code. The error "TypeError: Cannot ...

Angular's observables were unable to be subscribed to in either the constructor or ngOnInit() functions

Currently, I am incorporating an observable concept into my application. In this setup, a service is called by component1 to emit an event that is then subscribed to by component 2. Below is the code snippet for reference: Service Code export class Mes ...

Encapsulating functions with multiple definitions in Typescript

Struggling with wrapping a function that can have multiple return types based on input parameters in Typescript. Imagine wanting a function to return ReturnA for VariantEnum.a and ReturnB for VariantEnum.b. Consider this implementation of sampleFunction: ...

Bringing together the functionality of tap to dismiss keyboard, keyboard avoiding view, and a submit button

On my screen, there's a big image along with a text input and a button at the bottom. The screen has three main requirements: When the user taps on the input, both the input and button should be visible above the keyboard The user must be able to ta ...