Encountering unspecified values when subscribing to a BehaviorSubject and receiving it as an Observable

My goal is to display the name of the currently logged-in user in the header of my application. However, I encountered an issue where upon refreshing the page, the value would be lost due to SPA behavior (even though the data is stored in local storage). To address this, I created a function within my authentication service that retrieves the logged-in user from local storage and stores it for use. This function is called within the ngOnInit() of the header component. Despite subscribing to the values, I am encountering undefined results. The logic seems correct as I am re-assigning the values before retrieving them from the service.

Authentication Service

import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';

import { Observable } from 'rxjs/Observable';
import { BehaviorSubject } from 'rxjs/BehaviorSubject';

import { CurrentUserInterface } from '../interfaces/current-user.interface';

@Injectable()
export class AuthenticationService {
private rootUrl = 'testapi.com';

private currentUser = new BehaviorSubject<CurrentUserInterface>();
private isAuthorized: BehaviorSubject<boolean> = new BehaviorSubject(false);

constructor(private http: HttpClient) { }

setCurrentUser(): void {
    if(localStorage.getItem("currentUser")) {
        this.currentUser.next(JSON.parse(localStorage.getItem("currentUser")));
        this.isAuthorized.next(true);
    }
    else {
        this.isAuthorized.next(false);
    }
    console.log(this.currentUser.value); **//HAS VALUES**
}

getCurrentUser(): Observable<CurrentUserInterface> {
    return this.currentUser.asObservable();
}

checkAuth(): Observable<boolean> {
    return this.isAuthorized.asObservable();
}
}

Header Component

    import { Component, OnInit } from '@angular/core';
    import { Router } from '@angular/router';

    import { Observable } from 'rxjs/Observable';

    import { AuthenticationService } from '../_shared/services/authentication.service';

    import { CurrentUserInterface } from '../_shared/interfaces/current-user.interface';

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

  private currentUser = new Observable<CurrentUserInterface>;
  private isAuthorized: Observable<boolean> = false;

  constructor(private router: Router,
      private authenticationService: AuthenticationService) { }

  ngOnInit() {
      this.authenticationService.setCurrentUser();
      this.isAuthorized = this.authenticationService.checkAuth()
      .subscribe(
          isAuthorized => {
              this.isAuthorized = isAuthorized;
          },
          error => {
              console.log(error);
          }
      );

      this.currentUser = this.authenticationService.getCurrentUser()
      .subscribe(
          currentUser => {
              this.currentUser = currentUser;
          },
          error => {
              console.log(error);
          }
      );

      console.log(this.currentUser.value); **// UNDEFINED**
      console.log(this.isAuthorized.value); **// UNDEFINED**
   }

   logout() {
          this.authenticationService.logout();
          this.router.navigate(['../login'], { relativeTo: this.route });
   }

}

Answer №1

There is a mistake in how you are assigning the result of an observable to the observable itself. Here is what you currently have:

private currentUser = new Observable<CurrentUserInterface>();

Instead, you should do the following:

this.currentUser = this.authenticationService.getCurrentUser()
      .subscribe(
          currentUser => {
              this.currentUser = currentUser;
          },
          error => {
              console.log(error);
          }
      );

In addition, the console.log statement will run before the response is received.

Please take a look at the revised code below for a better approach:

import { Component, OnInit } from '@angular/core';
import { Router } from '@angular/router';

import { Observable } from 'rxjs/Observable';

import { AuthenticationService } from '../_shared/services/authentication.service';

import { CurrentUserInterface } from '../_shared/interfaces/current-user.interface';

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

  private currentUser: CurrentUserInterface;
  private isAuthorized: Observable<boolean> = false;

  constructor(private router: Router,
      private authenticationService: AuthenticationService) { }

  ngOnInit() {
      this.authenticationService.setCurrentUser();
      this.isAuthorized = this.authenticationService.checkAuth()
      .subscribe(
          isAuthorized => {
              this.isAuthorized = isAuthorized;
          },
          error => {
              console.log(error);
          }
      );

      this.authenticationService.getCurrentUser()
      .subscribe(
          currentUser => {
              this.currentUser = currentUser;
              console.log(this.currentUser); 
          },
          error => {
              console.log(error);
          }
      );
   }

   logout() {
          this.authenticationService.logout();
          this.router.navigate(['../login'], { relativeTo: this.route });
   }

}

Answer №2

Here is an improved version of your HeaderComponent ngOnInit() method. It's important to understand that calls to HTTP services are asynchronous, meaning command flow only continues inside the onNext, onError, or onComplete handlers. Any code directly after a subscribe call will execute before the service makes any calls to the observable. For instance, in your code, this.currentUser.value was undefined because you assigned this.currentUser to an observable, which does not have a .value property. To avoid such issues, consider strictly typing your variables.

ngOnInit() {
      this.authenticationService.setCurrentUser();
      // Removed assignment to this.isAuthorized
      this.authenticationService.checkAuth()
      .subscribe(
          isAuthorized => {
              this.isAuthorized = isAuthorized;
          },
          error => {
              console.log(error);
          }
      );

      // Removed assignment to this.currentUser
      this.authenticationService.getCurrentUser()
      .subscribe(
          currentUser => {
              this.currentUser = currentUser;
              // Moved console.log calls into the onNext handler to prevent them from being undefined, unless .value properties do not exist.
              console.log(this.currentUser.value); 
              console.log(this.isAuthorized.value);
          },
          error => {
              console.log(error);
          }
      );    

   }

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

The concept of Border-Merging within Material Design Tables

Is there a way to implement the following border style in a Material Design Table: border-collapse: collapse; I tried adding a border to .mat-cell: .mat-cell { border: 1px solid; } However, in some instances, the border appears to be 2px. The reas ...

Is there a way to communicate with the Microsoft bot from within the bot itself, ensuring that the message follows the dialog flow and receives the appropriate response?

It would make more sense if the title of this were "how can I ensure the bot responds smoothly in case context is lost or there's a server restart during a user interaction with the bot. It's confusing as it is and I need to break down the planni ...

conditionally trigger one observable in rxjs before the other

I am in need of assistance or guidance regarding a challenge I am facing with rxjs that I cannot seem to resolve. In essence, my goal is to trigger an observable and complete it before the original one is triggered. Scenario: I am currently working on a ...

No results returned by Mongoose/MongoDB GeoJSON query

I have a Schema (Tour) which includes a GeoJSON Point type property called location. location: { type: { type: String, enum: ['Point'], required: true }, coordinates: { type: [Number], required: true ...

Troubleshooting Cross Origin Error in C# and Angular

We are currently in the process of developing a website using Angular 5. The backend has been created utilizing .Net Web API and SQL Server. Our plan is to deploy the website on Azure, making it accessible both from the internet and the intranet. After co ...

There was a problem with the module '@angular/material' as it was unable to export a certain member

In creating a custom Angular Material module, I have created a material.module.ts file and imported various Angular Material UI components as shown below: import { NgModule } from '@angular/core'; import { CommonModule } from '@angular/commo ...

How to Add RouterModule to an Angular 10 Library without Ivy

I am developing a custom Angular 10 Library to be shared on a private NPM repository. I followed the instructions from Angular.io and used ng new my-workspace --create-application=false and ng generate library my-library with Angular CLI. In one of the co ...

The function getStaticPaths() will generate a 404 error, indicating that the page

I have encountered a persistent issue with the getStaticPaths() function throwing a 404 error. After investigating, I suspect that the problem may lie in the implementation of the getAllPostIds() function, which is supposed to generate an array of object ...

Managing arrays in local storage with Angular 2+

I seem to be missing a crucial element in my endeavor to save and retrieve an array in local storage within my Angular 4 application. The array is fetched from the server and stored in a variable named 'aToDo' with type 'any', like so: ...

The failure to import a node module in next.js with typescript

I'm currently working on integrating a package called bcrypt into my project. I have successfully installed it using npm install bcrypt. Although the package is visible in the node_modules folder, I am encountering an error when trying to import it i ...

Creating a hierarchical visualization in Angular using a JSON object array

I'm currently working on creating a hierarchical view of users. My ultimate goal is to utilize this hierarchy view or something similar. The challenge lies in how the JSON objects used to construct the hierarchy are structured. Here's an example ...

Is there a way to retrieve a data type from a class in TypeScript?

Within my code, there exists a class: class Person { name: string; age: number; gender: string; constructor(params: any){ this.name = params.name; this.age = params.age; this.gender = params.gender; } } My question is how ca ...

In React and TypeScript, when I pass a state as a prop, the data fails to render

Here is the useState function for managing the Data Interestingly, removing this state from the code doesn't affect rendering at all. const [cart, setCart] = useState([] as Product[]); This piece of code handles Mapping and Rendering the Data <Sin ...

What causes @typescript-eslint to retain old types/files in its cache and prevent successful compilation?

When I kick off my Typescript application using tsc -b -w, I always encounter an issue with @typescript-eslint not reacting to file changes accurately. It flags invalid types/syntax errors where there are none. Restarting the process sometimes doesn't ...

Angular application hosted on Apache2 with SSL configured to communicate with a Spring Boot API running on port 8080

After developing a small application using Angular and an API with Spring Boot that includes an embedded Tomcat server, I decided to deploy them on a Raspberry Pi while configuring an SSL certificate with Let's Encrypt. The deployment process involve ...

What is the process of extracting multiple attributes from an object that has been selected by a user using mat-options (dropdown list) in Angular?

Summary: A dropdown list contains objects, unsure how to capture multiple attributes of selected object. Current Implementation: I have successfully created a dropdown list that displays the details of an object retrieved through an API call: <mat-f ...

The OR operator in TypeORM allows for more flexibility in querying multiple conditions

Is there any OR operator in TypeORM that I missed in the documentation or source code? I'm attempting to conduct a simple search using a repository. db.getRepository(MyModel).find({ name : "john", lastName: "doe" }) I am awar ...

What is the process for changing the border color of a material selection?

Is it possible to customize the border color of a material select element? I attempted changing the border color using css from: https://i.sstatic.net/mw9jq.png to this: https://i.sstatic.net/8b7w7.png Any suggestions on how to achieve this? Appreciate an ...

"Exploring the differences between normalization structures and observable entities in ngrx

I'm currently grappling with the concept of "entity arrays" in my ngrx Store. Let's say I have a collection of PlanDTO retrieved from my api server. Based on the research I've done, it seems necessary to set up a kind of "table" to store th ...

Strategies for eliminating the 'hoek' vulnerabilities

I recently uploaded an Angular CLI 5 project to GitHub and received the following security alert: A security vulnerability was found in one of the dependencies used in net-incident/package-lock.json. It is recommended to update this dependency to address ...