Assigning a value to an Angular class variable within the subscribe method of an HTTP

Understanding the inner workings of this process has been a challenge for me. I've come across numerous articles that touch on this topic, but they all seem to emphasize the asynchronous nature of setting the class variable only when the callback is triggered.

So, how can one effectively set the class variable with data from the server-side?

Let me clarify, I grasp the reason why the value of this.isAuthorized behaves as it does. My struggle lies in finding the right method to properly assign my class variable this.isAuthorized.

Below is a snippet of the code for reference:

export class AuthService {
  private apiUrl = environment.api_uri;
  public isAuthorized: boolean = false;

  constructor(private http: HttpClient) { }

  public authorize(): void {
    this.http.get(this.apiUrl+'/auth')
             .subscribe(
               data => {
                this.isAuthorized = true;
                console.log("authorize => "+this.isAuthorized) //displays true
              },
              error => {
                console.log(error);
                console.log("authorize => "+this.isAuthorized) //displays false
              });
    console.log("authorize => "+this.isAuthorized) //display false
  }
}

Here's how I implement the above service:

export class AuthGuard implements CanActivate {
  constructor(private auth: AuthService, private router: Router){}

  canActivate(
    next: ActivatedRouteSnapshot,
    state: RouterStateSnapshot): Observable<boolean | UrlTree> | Promise<boolean | UrlTree> | boolean | UrlTree {
    this.auth.authorize();

    console.log( this.auth.isAuthorized ); // displays false

    if( this.auth.isAuthorized ) return true;

    return this.router.parseUrl("/403");
  }

}

Answer №1

Angular emphasizes the importance of RxJS, which requires a shift in our approach to services, methods, and variables.

Let's focus on the AuthService.

The AuthService communicates with the backend to verify the user's authentication status. The AuthGuard depends on this service for its functionality. Angular expects specific return types within the canActivate syntax:

Observable<boolean | UrlTree> | Promise<boolean | UrlTree> | boolean | UrlTree

Currently, the AuthGuard returns boolean | UrlTree, but it can also return

Observable<boolean | UrlTree>
. This leads us to an important realization. While the AuthService internally uses an Observable, it does not pass it along. Let's make some adjustments.

export class AuthService {
  private apiUrl = environment.api_uri;

  constructor(private http: HttpClient) { }

  public authorize(): Observable<boolean> {
    return this.http.get(this.apiUrl+'/auth')
             .pipe(
               map(data => {
                 let authorized = .... // add your logic here
                 return authorized;
              }),
              catchError(error => {
                 return of(false); 
              }));
  }
}

By modifying the AuthService to return an Observable, we can apply additional operators and utilize it within the AuthGuard.

export class AuthGuard implements CanActivate {
  constructor(private auth: AuthService, private router: Router){}

  canActivate(
    next: ActivatedRouteSnapshot,
    state: RouterStateSnapshot): Observable<boolean | UrlTree> | Promise<boolean | UrlTree> | boolean | UrlTree {
    return this.auth.authorize().pipe(
       map(isAuthorized => isAuthorized || this.router.parseUrl("/403"))
    );
  }

}

This adjustment transforms the service into a stateless entity that functions independently upon method calls.

Sometimes, services require data storage for new entities to access past information. To address this, we introduce concepts like Subject and BehaviorSubject, or leverage the shareReplay operator for efficient data handling.

For further insights, visit this link: here

An RxJS Subject facilitates multicasting values to multiple Observers, unlike regular unicast Observables where each Observer initiates a separate execution of the Observable.

Implementing a Subject as outlined in the documentation:

const subject = new Subject<number>();

const subscriber1 = subject.subscribe(val => console.log(val))
const subscriber2 = subject.subscribe(val => console.log(val))

// Triggers callbacks of the above subscribers
subject.next(5);

However, latecomers will miss previous emissions, limiting their accessibility to the last emitted value.

const subscriber3 = subject.subscribe(val => console.log(val))

To enable newcomers to retrieve previous values, consider implementing a BehaviorSubject.

Visit this resource for more details: here


const subject = new BehaviorSubject(123);

subject.subscribe(console.log);
subject.subscribe(console.log);

subject.next(456);

subject.subscribe(console.log);

subject.next(789);

Utilizing BehaviorSubject ensures initial values are accessible by new subscribers. However, for scenario involving static data shared amongst components without modification, employing shareReplay becomes instrumental.

Refactoring the AuthService involves creating a member variable isAuthorized encapsulating the HTTP call with error handling wrapped inside a shareReplay operator.

export class AuthService {
  private apiUrl = environment.api_uri;
  private isAuthorized = this.http.get(this.apiUrl+'/auth')
             .pipe(
               map(data => {
                 let authorized = .... // add your logic here
                 return authorized;
              }),
              catchError(error => {
                 return of(false);               
              }),
              shareReplay()

  );

  constructor(private http: HttpClient) { }

}

The usage remains consistent regardless of repetition, ensuring the singular execution of the underlying HTTP request within the service is maintained.

authService.isAuthorized.subscribe(_ => .....)

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

Accessing the property of an object with TypeScript

I am working with an array of objects, where each object contains two properties: {key:count} When configuring my chart, I need to set the data source in this format: {meta: "unknown", value: [the count of unknown]}, {meta: "male", value: [the count of ...

What is the process for importing a component at a later time?

I am attempting to import components with a delay in a seamless manner. My goal is to import the components discreetly so that they load smoothly in the background while viewing the homepage. I experimented with lazy loading, but found that it caused dela ...

Angular 12: How to detect when a browser tab is closing and implement a confirmation dialog with MatDialog

I have a scenario where I am checking if the browser tab is closed using the code below. It currently works with windows dialog, but I would like to incorporate MatDialog for confirmation instead. @HostListener('window:beforeunload', ['$eve ...

When trying to save a child entity, TypeORM's lazy loading feature fails to update

I've been troubleshooting an issue and trying various methods to resolve it, but I haven't had any success. Hopefully, someone here can assist me. Essentially, I have a one-to-one relationship that needs to be lazy-loaded. The relationship tree ...

Angular 6 Exporting Data to CSV File

Recently, I've started working with Angular and I'm currently involved in a project that requires CSV export functionality. The project uses Angular 6 on the frontend and Laravel on the backend. Below is the Laravel function I wrote using mattwe ...

Exploring the Functionality of Algolia Places within a Next.js Application

Currently, I am working on integrating the Algolia Places functionality into my next.js project using TypeScript. To begin, I executed npm install places.js --save Next, I inserted <input type="search" id="address-input" placeholder ...

having trouble retrieving information from mongodb

Currently working with nestjs and trying to retrieve data from a collection based on the 'name' value. However, the output I am getting looks like this: https://i.stack.imgur.com/q5Vow.png Here is the service code: async findByName(name):Promi ...

Creating layers of object declarations

Looking for assistance on the code snippet below. type Item = { id: number; size: number; } type Example = { name: string; items: [ Item ]; } var obj: Example = { name: "test", items: [ { i ...

What is the reason for the ngFor loop in Ionic 5 component not functioning properly?

I'm facing an issue with the *ngFor directive in one of my components. It's strange because it works fine in other components. Can anyone help me figure out what I might be doing wrong? @Component({ selector: 'app-age-picker', temp ...

Ways to link a Database with an Angular Web App

Hello all, I am embarking on a project where I need to showcase and modify data from an existing PostgreSQL database within an Angular Web Application. As a complete newcomer to Angular and related technologies, I have taken the initial steps by downloadin ...

Adding *ngIf dynamically within a directive allows for conditional rendering based on certain parameters or

Is there a way to dynamically include an *ngIf on an element that is decorated with an attribute directive? I decided to test this with a simple experiment: @Directive({ selector: '[lhUserHasRights]' }) export class UserHasRightsDirective i ...

Passing parent form controls from an Angular 4 FormGroup to a child component

I have implemented Angular Reactive Forms FormGroup and FormArray in my project. The issue I am facing is related to splitting my form fields into child components and passing the parent form controls to them. I expected this to be a straightforward proces ...

What could be causing the images to not display on my HTML page?

My program is designed to display an image based on the result of the random function. Here is my HTML: <div> <h2>Player 0:</h2> <div id="MainPlayer0"></div> </div> Next, in my TypeScript fi ...

Switch from Gulp-TSLint to Gulp-ESLint for enhanced code analysis!

I am currently in the process of updating a Gulp task that uses gulp-tslint to now use gulp-eslint. The code snippet below outlines the changes I need to make: const { src } = require('gulp'); const config = require('./config'); const ...

Angular Components: Achieving Full Height Issue with TabsResolved

I'm facing a challenge in making my tab fill the full height of the content underneath it. I've tried different solutions like setting height: 100%, but nothing seems to be working. Here is the HTML code: <mat-tab-group [selectedIndex]=" ...

Implementing conditional asynchronous function call with identical arguments in a Typescript React project

Is there a way in React to make multiple asynchronous calls with the same parameters based on different conditions? Here's an example of what I'm trying to do: const getNewContent = (payload: any) => { (currentOption === myMediaEnum.T ...

Enabling universal pre-rendering results in the establishment of empty transfer-state

I have implemented Angular Universal in my application along with prerendering. The specific paths that I have set for prerendering are as follows: / /about /login Interestingly, the only path that utilizes transfer state is: /:id Upon building and run ...

Initializing the ngOnInit function with 'this' keyword

I've encountered a scope issue where the lines "this.catVotes = catData" are within another function, preventing me from directly assigning it to "catVotes: Number;" Any suggestions on how I can overcome this challenge? catVotes: Number; dogVotes: N ...

When using Angular Reactive Forms with a number type control, the form will trigger a re-render when the

My Angular(v7) Reactive Form (or template-only form) is experiencing issues with re-rendering and validation on blur when using an <input> with type="number". The error feedback <div> next to the input contains a value suggestion button, whic ...

Communication between components through a shared service

Imagine you find yourself in one component and need to trigger a method from another component. There are multiple ways to achieve this, which are explained in more detail on this page. Here, I will demonstrate the simplest possible example of how to make ...