Using async-await in canActivate() within Angular 7

I am currently working on a code that verifies whether the browser contains user information. If not, the browser will make an API call to retrieve the user information from the server. Only users with valid information are granted access to navigate to different pages.

My issue lies in the fact that the canactivate() function does not pause for the API call to finish. I have attempted to use Resolvers as suggested in other discussions, but unfortunately, this did not resolve the problem either.

In an attempt to address this, I implemented async-await within the code, however, this approach also proved ineffective. The canactivate() function continues executing before the getUser() function has completed. Is there a mistake in the syntax of the async-await implementation in the following code snippet?

@Injectable()
export class AuthGuard implements CanActivate {

  constructor(private router: Router, private refService: RefService, private userInfoService: UserInfoService) {
  }

  canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<boolean> | Promise<boolean> | boolean {

    this.getUser();
    console.info('canActivate completed');
    return this.userInfoService.getUserInfo() !== null;
  }

  async getUser() {
    if(!this.userInfoService.getUserInfo()) {
      await this.refService.getTamUser().toPromise( ).then(data => {
        this.userInfoService.setUserInfo(data);
      });
    }
  }
}

Answer №1

In the scenario where canActivate is not waiting for getUser to resolve, I suggest implementing it in the following manner:

canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<boolean> | Promise<boolean> | boolean {
    const userInfo = this.userInfoService.getUserInfo()
    if (userInfo) {
      return userInfo !== null;
    } else {
      return this.refService.getTamUser().pipe(switchMap((data: any) => {
        this.userInfoService.setUserInfo(data);
        return of(data !== null);
      })) 
    }
  }

The logic first checks if user information is available. If not, it retrieves the user info from the reference service and returns a new observable of type boolean. A false boolean indicates either unauthorized access or failure in fetching data from the reference service.

For more information on switchMap, refer to this resource. In essence, switchMap transforms data from the original observable into a new observable.

Answer №2

If you want to incorporate your asynchronous logic within the canActivate function itself, you can utilize a promise. This way, the canActivate function will only return in the then or catch block of the promise. Remember to also return as an observable.

 @Injectable()
export class AuthGuard implements CanActivate {

  constructor(private router: Router, private refService: RefService, private userInfoService: UserInfoService) {
  }

  canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<boolean> | Promise<boolean> | boolean {
  if (!this.userInfoService.getUserInfo()) {
    this.refService.getTamUser().toPromise()
    .then(data => {
      if (data) {
        this.userInfoService.setUserInfo(data);
        console.info('canActivate completed');
        return of(true);
      } else {
        return false
      }
    })
    .catch(() => {
      return false;
    })
  } else {
    return of(this.userInfoService.getUserInfo() !== null);
  }
}

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

Having difficulty accessing the value of a table td element in HTML using a jQuery selector

When creating a table, I utilize ng-repeat to generate table rows. Whenever the dropdown changes, a function is triggered which applies certain conditions. Based on these conditions, an object is added to an array that is bound to a scope variable. Here i ...

In the Safari browser, an error occurred when trying to locate the variable Tmpo, which is a plain JavaScript class

I created a small Angular application that utilizes a basic JavaScript class located in my assets/js directory. Everything functions flawlessly in my local environment when using ng-serve. However, upon building and deploying the app (ng build --prod), I e ...

What is the correct way to utilize a variable as a parameter in React Query while employing the axios.request(options) method?

I'm currently working on a React Query component with the following structure: function test () { const [var, setVar] = useState("") const options = { method: "GET", url: "https://api.themoviedb.org/3/search/tv" ...

Validator returns undefined when expressing invalid data

Having an issue with validation, here is the code snippet: routes.js var express = require('express'); var router = express.Router(); var hello_controller = require('../api/controllers/helloController'); var { validationRules, validat ...

Having trouble getting web components registered when testing Lit Element (lit-element) with @web/test-runner and @open-wc/testing-helpers?

Currently, I am working with Lit Element and Typescript for my project. Here are the dependencies for my tests: "@esm-bundle/chai": "^4.3.4-fix.0", "@open-wc/chai-dom-equals": "^0.12.36", "@open-wc/testing-help ...

What's the best way for me to figure out whether type T is implementing an interface?

Is it possible to set a default value for the identifier property in TypeScript based on whether the type extends from Entity or not? Here's an example of what I'm trying to achieve: export interface Entity { id: number; // ... } @Compon ...

Adding connected types to a list using Typescript

Question regarding Typescript fundamentals. In my code, I have a list that combines two types using the & operator. Here is how it's initialized: let objects: (Object & number)[] = []; I'm unsure how to add values to this list. I attem ...

Tips for utilizing ng class within a loop

Having some trouble with my template that loops through a JSON file using json server. The issue I'm facing is related to correctly applying ng class when clicking on icons. Currently, when I click on an icon, it adds a SCSS class but applies it to al ...

encountering a problem integrating react-dropzone into a project using react-16.13.1

I'm having an issue adding the package https://www.npmjs.com/package/react-dropzone to my TypeScript React project using Yarn as the package manager. I ran the command yarn add @types/react-dropzone, but it ended up installing a deprecated package h ...

Creating web components with lit-element, leveraging rollup, postcss, and the tailwind framework for packaging

I have been attempting to package a functional web component that was developed using the lit-element/lit-html with the tailwind framework utilizing the postcss plugin from the rollup packager. Upon conducting a rollup, I discovered the compiled js and ht ...

Prisma allows for establishing one-to-many relationships with itself, enabling complex data connections

I am in the process of developing a simple app similar to Tinder using Prisma. In this app, users can swipe left or right to like or dislike other users. I want to be able to retrieve matches (users who also like me) and candidates (all users except myself ...

Angular Persistent States in Angular version 2 and beyond

Currently, I am in the process of transitioning an Angular 1.5.X application to Angular 4. Within my app, I incorporate Angular Ui-Router Sticky State from https://github.com/ui-router/sticky-states to prevent loss of content within my view when navigating ...

Rearranging data received from an Observable

TL;DR I am working on an Angular app where I need to shuffle an array of names retrieved from a network request and ensure that each group of 6 names selected is unique. However, I noticed duplicates in the selections. Here's a CodePen example using o ...

Comparing two arrays in Angular through filtering

I have two arrays and I am trying to display only the data that matches with the first array. For example: The first array looks like this: ["1", "2" , "3"] The second array is as follows: [{"name": "xyz", "id": "1"},{"name":"abc", "id": "3"}, ,{"name ...

ng serve generates an error related to node_modules appearing to be empty

Recently, I embarked on my Angular 4 learning journey and took the following steps: Firstly, I installed Node.js (version 8). Then, I executed the command npm install -g @angular/cli Using the CLI, I created a new project called: ng new my-first-app Ho ...

Displaying error messages from custom validators in Angular 6 reactive forms using their own data

In my application, I am working with a FormArray within a FormGroup. Each FormArray consists of multiple FormGroup instances that can be added dynamically. One of the features in my application is a Custom Validator that checks for repeated data across al ...

To resolve the issue of expired tokens, we must implement a mechanism in Angular to detect when a token has expired. When a token expiration

When utilizing an angular interceptor to include the authorization token in the header, everything functions smoothly until the token expires. Following the expiration of the token, Laravel returns a token_expired error. My goal is to detect this error wit ...

Ensuring seamless collaboration between Typescript node modules

Is there anyone who has successfully set up a configuration where module 1, using TypeScript, is referencing another module 2 also with TypeScript, and both are utilizing tsd types like node.d.ts? I have been able to compile and use both modules without a ...

Enhancing Angular input validators with updates

Working on a project with Angular 6, I have set up an input field using mat-input from the Angular Material framework and assigned it an id for FormGroup validation. However, when I initialize my TypeScript class and update the input value, the validator d ...

Having trouble mocking Node fs Modules using Sinon

I am facing an issue with mocking the promises methods of the node fs module in my code. When my appData.ts file is executed, it calls the actual fs.promises.mkdir method instead of the mock function defined in \__tests__/appData.test.js. I suspect ...