Unexpected behavior with Angular 10 behavior subject - encountering null value after invoking .next(value) function

Can anyone help me solve the mystery of why my user value turns null after I log in?

This is the login page where an API is called and the result is obtained as shown below:

https://i.stack.imgur.com/kDjSy.png

Here is the authentication service implementation:

export class AuthenticationService {
  private userSubject: BehaviorSubject<User>;
  public user: Observable<User>;

  constructor(
    private router: Router,
    private http: HttpClient
  ) {
    this.userSubject = new BehaviorSubject<User>(JSON.parse(localStorage.getItem('user')));
    this.user = this.userSubject.asObservable();
  }
  public get userValue(): User {
    return this.userSubject.value;
  }

  GetUsername(): string {
    if (localStorage.getItem('currentUser') != null)
      return JSON.parse(localStorage.getItem('currentUser')).UserName;
    return null;
  }
  login(username: string, password: string) {

    return this.http.post<any>(`${Statics.ApiUrl}users/authenticate`, { Username: username, Password: password }, { responseType: "json" })
      .pipe(map(user => {
        debugger;
        localStorage.setItem('user', JSON.stringify(user));
        this.userSubject.next(user);
        return user;
      }));
  }

  logout() {
    //remove user from local storage to log user out
    localStorage.removeItem('user');
    this.userSubject.next(null);
    this.router.navigate(['/login']);
  }

  signUp(RegisterModel: RegisterModel): Observable<User> {
    return this.http.post<User>(`${Statics.ApiUrl}users/signup`, RegisterModel, { responseType: "json" });
  }
  verifyUserPhone(username: string, password: string, verificationCode: string): Observable<User> {
    return this.http.put<User>(Statics.ApiUrl + 'users/verifyUserPhone', { username, password, verificationCode }, { responseType: "json" });
  }
}

Now let's take a look at the authguard used:

export class AuthGuard implements CanActivate {
    constructor(
        private router: Router,
        private authenticationService: AuthenticationService
    ) { }

    canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot) {
      debugger;
      const user = this.authenticationService.userValue;
      var token_expiration=new Date(user?.TokenExpirartion);
      var now=new Date();
        if (user && token_expiration>now) {
            // check if route is restricted by role
            if (route.data.roles && route.data.roles.indexOf(user.role) === -1) {
                // role not authorised so redirect to home page
                this.router.navigate(['/']);
                return false;
            }

            // authorized so return true
            return true;
        }

        // not logged in so redirect to login page with the return url
        this.authenticationService.logout();
        this.router.navigate(['/login'], { queryParams: { returnUrl: state.url } });
        return false;
    }
}

Even though I successfully login and store the user data in local storage, the authguard still returns null for the user. It only works after refreshing the page. The logout function also requires a page refresh to fully log out the user.

Below is an image showing the issue:

https://i.stack.imgur.com/dG82h.png

This is the code snippet for my login button:

this.AuthenticationService.login(this.username, this.password)
  .pipe(first())
  .subscribe({
    next: () => {

      // get return url from query parameters or default to home page
      const returnUrl = this.route.snapshot.queryParams['returnUrl'] || '/';
      this.router.navigateByUrl(returnUrl);
    },
    error: error => {
      this.error = error;
      this.submited = false;
    }
  });

Every time the authguard checks the userSubject after logging in, it returns null. The issue persists unless I refresh the page, which finally evaluates the userSubject correctly. Any suggestions on how to fix this problem are welcome!

After refreshing the page, the value looks like this:

https://i.stack.imgur.com/HGeM5.png

Answer №1

Do you deliver your offerings directly from the root? If not, there is a possibility of receiving several duplicates.

@Injectable({
  providedIn: 'root'
})
export class UserAuthentication {
...

Answer №2

The authentication service appears to be lagging in fetching data, leading to the route being rejected due to receiving null values.

To address this issue, consider incorporating a delay before confirming or denying a route.

Answer №3

The issue here is that your authguard is synchronous, causing canActivate to be called before the user is actually loaded. To resolve this, you should modify your canActivate method to return an observable of boolean instead of a boolean value. Firstly, create a function that returns an observable of the user if it has already been loaded, or loads the user using the login service:

public getOrFetchUser(): Observable<User> {
   // Ensure that we always return an observable
   return this.userSubject.value ? of(this.userSubject.value) : this.login();
}

Then update your canActivate guard as follows:

canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<boolean> {
return this.authenticationService.getOrFetchUser().pipe(
   map((user) => {
      console.log('user, ', user);
      // Update the return statement with your validation logic
      return true; 
});

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

Struggled with setting up the WebSocket structure in typescript

Issue Running the code below results in an error: index.tsx import WebSocket from 'ws'; export default function Home() { const socket = new WebSocket('ws://localhost:1919/ws'); return ( <div>Home</div> ); } ...

Problem: Unable to locate the TypeScript declaration file

I am facing an issue with my TypeScript configuration. I have two files in /src/models/: User.ts and User.d.ts. In User.ts, I am building a class and trying to use an interface declaration for an object defined in User.d.ts. However, User.ts is unable to a ...

Change Observable<String[]> into Observable<DataType[]>

I'm currently working with an API that provides me with an Array<string> of IDs when given an original ID (one to many relationship). My goal is to make individual HTTP requests for each of these IDs in order to retrieve the associated data from ...

Switch up div positioning by toggling in Angular 2

https://i.stack.imgur.com/LO0qy.jpg When the arrow button is clicked, the image should be set as the primary image and this value should reflect in the backend system. I am struggling to find a solution for this. I tried using Dragula, but it didn't ...

What could be the reason behind encountering the error stating "Type 'Number' does not have any compatible call signatures"?

Hey there, I am currently working on an angular component and I have this code snippet: private approvals: Approval[] = []; ngOnInit() { this.getUsersApprovals(this.userid); } getUsersApprovals(userid) { this.approvalsService.getUsersApp ...

What could be causing the conditional div to malfunction in Angular?

There are three conditional div elements on a page, each meant to be displayed based on specific conditions. <div *ngIf="isAvailable=='true'"> <form> <div class="form-group"> <label for ...

Utilizing Observables for AngularJS Services across Multiple Components in a View

My current challenge lies in Angular, where I am struggling to implement Observables in a service that will be utilized by multiple components. The issue at hand involves having Component A and Component B nested inside Component C (in a tab style layout). ...

Managing elements within another element in Angular

I am currently exploring the use of Component Based Architecture (CBA) within Angular. Here is the situation I am dealing with: I have implemented three components each with unique selectors. Now, in a 4th component, I am attempting to import these compon ...

Creating a personalized tooltip in Angular for a bubble chart with ApexCharts

I'm currently working on customizing the tooltip for a bubble chart using the ApexCharts library. Here is the link to my project. ...

Angular 6 Subscription Service Does Not Trigger Data Sharing Events

Is there a way to set a value in one component (Component A) and then receive that value in another component (Component B), even if these two components are not directly connected as parent and child? To tackle this issue, I decided to implement a Shared ...

The function cannot be called on a type that does not have a callable signature. The specified type, 'number | Dispatch<SetStateAction<number>>', does not have any compatible call signatures

Currently, I am working on setting up state to be passed through context in React using hooks. However, when I attempt to use the dispatched state updater function, an error is thrown: Cannot invoke an expression whose type lacks a call signature. Type &a ...

Creating a digital collection using Vue, Typescript, and Webpack

A short while back, I made the decision to transform my Vue project into a library in order to make it easier to reuse the components across different projects. Following some guidelines, I successfully converted the project into a library. However, when ...

Issue with Child Component Loading Once CUSTOM_ELEMENTS_SCHEMA is Added to App Module

One of my components, known as HostComponent, works perfectly fine when set as the startup component in my application. However, I decided to create a new module called AppModule and nested the host component within the app component: import { Component, ...

The menu's mouseover event is activated when hovering over the inner element

Whenever a user hovers over a specific element, a menu appears. This menu remains visible only as long as the user is hovering over it. However, the issue arises when there are elements within the menu itself, causing the menu to hide when the user hovers ...

Ways to selectively deactivate client-side functionality

I have implemented a server-side rendered app with transitions, including a 404 error page that I placed in a lazy module to avoid increasing the size of loaded JavaScript. While this setup is functioning correctly, there is some flickering when the clien ...

A Guide to Performing Dual API Calls within Angular for a Single Component

Is there a way to make two separate API calls within the same Angular component? For instance, I have an order component that is rendered twice in a tabular manager on a page. Using ngif condition, I display different data for TAB1 and TAB2. The issue is ...

Tips for identifying and handling a 400 bad request error in an HTTP response within an Angular 2 application

I attempted to handle the error 400 bad request in this manner: catch((error: any) => { if (error.status === 500) { return Observable.throw(new Error(error.status)); } else if (error.status === 400) { console.log( 'err ...

Attempting to perform an API invocation on a distant endpoint utilizing NestJS

var unirest = require("unirest"); var req = unirest("GET", "https://edamam-edamam-nutrition-analysis.p.rapidapi.com/api/nutrition-data"); req.query({ "ingr": "1 large apple" }); req.headers({ &qu ...

Guide on filtering FlatList Data in react native by selecting multiple categories from an array

User Interface Image I am looking to implement a filter functionality in the FlatList data based on top categories, where the filter button allows for multiple selections. The FlatList data is stored in the HotelData array, and the categories are also re ...

Determine whether there are a minimum of two elements in the array that are larger than zero - JavaScript/Typescript

Looking for an efficient way to determine if there are at least two values greater than 0 in an array and return true? Otherwise, return false. Here's a hypothetical but incorrect attempt using the example: const x = [9, 1, 0]; const y = [0, 0, 0]; c ...