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

Tips for selecting the best className type for material-ui components

Currently, I am integrating material-ui into a react app that is built using typescript. Within the material-ui framework, there is a feature called withStyles which allows styles to be injected into a component through its className. However, I am facing ...

I'm puzzled by how my observable seems to be activating on its own without

Sorry if this is a silly question. I am looking at the following code snippet: ngOnInit(): void { let data$ = new Observable((observer: Observer<string>) => { observer.next('message 1'); }); data$.subscribe( ...

Exploring the power of Next.js, Styled-components, and leveraging Yandex Metrica Session Replay

I'm currently involved in a project that utilizes Next.js and styled-components. In my [slug].tsx file: export default function ProductDetails({ product }: IProductDetailsProps) { const router = useRouter(); if (router.isFallback) { return ( ...

What is the best way to swap out the if else statement with a Ternary operator within a JavaScript function?

Is there a way to replace the if else statement in the function using a Ternary operator in JavaScript? private getProductName(productType: string): string { let productName = 'Product not found'; this.deal.packages.find(p => p.isSele ...

Tips for utilizing <Omit> and generic types effectively in TypeScript?

I'm currently working on refining a service layer in an API with TypeScript by utilizing a base Data Transfer Object. To prevent the need for repetitive typing, I have decided to make use of the <Omit> utility. However, this has led to some per ...

Is there a way to inject 'cmd' into the browser for Sentry (@sentry/nextjs package) by using a personalized webpack setup in Next.js?

My package json includes the following dependencies: "webpack": "5.58.1", "@sentry/nextjs": "6.13.3", "typescript": "4.0.5", "next": "11.0.1", After running next build without ...

How Keyof can render an object undefined and prevent accurate verification

Encountering TS2532 error: Object is possibly 'undefined' while attempting to access an object's value by dynamically selecting the key. TypeScript seems to be restricting me from checking the field values, and I'm unsure of the underly ...

My type is slipping away with Typescript and text conversion to lowercase

Here is a simplified version of the issue I'm facing: const demo = { aaa: 'aaa', bbb: 'bbb', } const input = 'AAA' console.log(demo[input.toLowerCase()]) Playground While plain JS works fine by converting &apo ...

Whenever npm or ng-packagr are updated, the publishing process may attempt to use an incorrect package name or version

Following the transition to [email protected], [email protected], and Angular@13, I am encountering difficulties while attempting to publish a package generated by ng-packager to the npm repository. This is the content of the package.json file: ...

Utilize useEffect to track a single property that relies on the values of several other properties

Below is a snippet of code: const MyComponent: React.FC<MyComponentProps> = ({ trackMyChanges, iChangeEverySecond }) => { // React Hook useEffect has missing dependencies: 'iChangeEverySecond' useEffect(() => { ...

Proper positioning of try/catch block in scenarios involving delayed async/await operations

For the past six months, I have been utilizing async/await and have truly enjoyed the convenience it provides. Typically, I adhere to the traditional usage like so: try { await doSomethingAsync() } catch (e) {} Lately, I've delved into experimenti ...

Exploring generic types using recursive inference

The scenario: export type SchemaOne<T> = | Entity<T> | SchemaObjectOne<T>; export interface SchemaObjectOne<T> { [key: string]: SchemaOne<T>; } export type SchemaOf<T> = T extends SchemaOne<infer R> ? R : nev ...

Angular child component displaying of table data is not looping properly

Currently, I am using Angular 13 along with Bootstrap 3 to develop an application that consists of two main components: form-component (dedicated to inputting user details) and list-component (designed to showcase all data in a table format). Within the HT ...

Can a class be passed to the binding attribute in Angular framework?

We currently have a system in place that is dependent on a numeric value being set to either 1 or 2 in order to display specific icons. This method is functional. <span *ngIf="config.icon" [class.fas]="true" [class.fa-plus]="icon===1" ...

How can I trigger a function after all nested subscriptions are completed in typescript/rxjs?

So I need to create a new user and then create two different entities upon success. The process looks like this. this.userRepository.saveAsNew(user).subscribe((user: User) => { user.addEntity1(Entity1).subscribe((entity1: EntityClass) => {}, ...

The classification of a property is determined by the types of the other properties present

I am trying to figure out a way in Typescript to create a general component that takes a prop called component, with the remaining props being specific to that component. How can I achieve this? For example: <FormField component={Input} ... /> Thi ...

Issue: supportsScrollBehavior has been declared as non-configurable

I am attempting to monitor a function called supportsScrollBehavior within the angular platform service using the following code snippet - import * as platform from '@angular/cdk/platform'; describe('Supporting Scroll Behaviour', ( ...

Intercepting HTTP requests on specific routes with Angular 4+ using an HTTP Interceptor

I've developed an HTTP_INTERCEPTOR that needs to function on certain routes while excluding others. Initially, it was included in the main app module file. However, after removing it from there and adding it to specific modules, the interceptor conti ...

What steps should I take to resolve the ChunkLoadError related to signalr?

Recently, I encountered an issue while running my nx site locally. It seems that any federated app using signalR is now throwing a ChunkLoadError. I attempted various solutions such as changing the version of signalR, reloading the page, clearing cache, a ...

Reactive Extensions - Incorporating interdependent synchronous calls into a fork join without the need for nesting

Looking for a way to avoid nesting synchronous calls that rely on each other and wanting to subscribe at the end result, similar to forkjoin. I came across a solution on stackoverflow (Wait for nested subscribe in angular before continuing with main subscr ...