Executing an HTTP POST request within an Angular 7 route guard: A step-by-step guide

In my Angular 7 application, I have implemented a route guard that is dependent on the response of a post call. The guard is structured like this:

canActivate = (_router: ActivatedRouteSnapshot): boolean => {
    console.log('in link expiry guard')
    let userEmail = _router.paramMap.get('email');
    let isAllow;

    console.log('params : ', userEmail)
    userEmail = this._utilityService.decryptMsgByCryptoJs(userEmail);
    console.log('user email : ', userEmail)
    this._dataService.post(this._const.userResetPasswordLinkExpiry, { email: userEmail }).subscribe(resp => {
        console.log('verify response : ',resp)
        if (resp.success) {
            console.log('in success')
            isAllow = true;
        } else {
            isAllow = false;
        }
    })
    console.log('allow flag  : ',isAllow)
    if (isAllow) {
        console.log('in allow')
        return true;
    } else {
        console.log('in not allow')
        this._utilityService.navigate('/login');
        this._dataService.exhangeResetPasswordObsMsg({ event: 'linkExpired' });
        return false;
    }
}

The issue arises when the HTTP post call is still in progress and the guard executes completely, returning false. This leads to complications as the response from the post call arrives later. How can I handle this situation effectively so that the route can be determined based on the post call response?

Answer №1

If you wish to execute an Http request within your canActivate method, you must return an Observable<boolean> instead of a simple boolean. This is necessary because the action is asynchronous.

Moreover, if you want to redirect in case of failure, you should return

Observable<boolean | UrlTree>
instead.

Basic Implementation

constructor(private router: Router) { }

canActivate(route: ActivatedRouteSnapshot, 
    state: RouterStateSnapshot): Observable<boolean | UrlTree> {
  return this.http.post(url, body).pipe(
    map((resp: any) => resp.success ? true : this.router.parseUrl('/path'))
  );   
}

In this example, we return the observable http request (which will be invoked by the router through subscription), and based on the response, either:

  • true - allowing the router to proceed to the protected route
  • UrlTree - instructing the router to navigate to a specific route

Application in Your Scenario

To apply this concept in your scenario, additional steps are required within the pipe due to the presence of an extra service call.

// TODO: inject other services
constructor(private router: Router) { }

canActivate(route: ActivatedRouteSnapshot, 
      state: RouterStateSnapshot): Observable<boolean | UrlTree> {
    const userEmail = route.paramMap.get('email');

    // Assuming this is a synchronous operation
    userEmail = this._utilityService.decryptMsgByCryptoJs(userEmail);

    const url = this._const.userResetPasswordLinkExpiry;
    const body = { email: userEmail };

    return this._dataService.post(url, body).pipe(
      // initial mapping of response to true/false
      map((resp: any) => resp.success),

      // trigger an action if false
      tap(success => {
        if (!success) {
          // Synchronous assumption here. Use switchMap for async
          this._dataService.exhangeResetPasswordObsMsg({ event: 'linkExpired' });
        }
      }),
      // final mapping to boolean | UrlTree
      map(success => success ? true : this.router.parseUrl('/login'))
    );   
}

This code includes some assumed synchronous service calls. It illustrates how to handle asynchronous operations within the canActivate function and decide whether to allow navigation or choose an alternative route.

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

How to override or redefine a TypeScript class with generics

Presently, I am immersed in a project involving three.js and TypeScript. It has come to my attention that for organizing elements, the Group class is highly recommended. However, I have noticed that the type definitions for Group do not include a feature l ...

Exploring the redirection behavior of Angular authentication guards

My implementation involves an Angular authenticated guard. @Injectable({ providedIn: 'root' }) export class AuthenticatedGuard implements CanActivate, CanActivateChild { constructor(@Inject('Window') private window: Window, ...

Error encountered in Typescript when attempting to invoke axios - the call lacks a suitable overload

When I make a call to axios, I include a config object like this: const req = { method, url, timeout: 300000, headers: { 'Content-Type': 'application/json' } } axios(req) An error in TypeScript is thrown stating that "No overload matc ...

Troubleshooting font color issues with PrimeNG charts in Angular

I have a chart and I am looking to modify the color of the labels https://i.sstatic.net/vsw6x.png The gray labels on the chart need to be changed to white for better readability Here is my code snippet: HTML5: <div class="box-result"> ...

Closures are like the "use" keyword in PHP or the capture list in C++, but they play a similar role in JavaScript and other transpiler languages

PHP has a useful feature with the use keyword, which allows for the usage of 'external' variables in closures. For example: $tax = 10; $totalPrice = function ($quantity, $price) use ($tax){ //mandatory 'use' return ($price * $quan ...

Issue with Angular: Mobile view toggle button in the navbar is unresponsive

While the dropdowns are functioning correctly in web mode, the navbar toggle isn't working as expected in small screens or mobile mode. I've been trying to figure out the issue by referring to code from CodePen that I am learning from. Despite i ...

No call signatures found for TypeScript custom React hook

Trying out this new custom hook for API requests import { useState, useCallback } from 'react'; interface OptionsShape { method: 'GET' | 'POST'; } interface InitStateShape { data: any; success: boolean; loading: bool ...

What is the process for finding GitHub users with a specific string in their name by utilizing the GitHub API

I'm looking to retrieve a list of users whose usernames contain the specific string I provide in my query. The only method I currently know to access user information is through another endpoint provided by the GitHub API, which unfortunately limits t ...

Leveraging a React hook within a Next.js API route

I am looking for a way to expose the data fetched by a React.js hook as a REST endpoint using Next.js. To create a REST endpoint in Next.js, I can easily use the code below in pages/api/index.tsx export default function handler(req: NextApiRequest, res: N ...

the object '[object Object]' of a distinct supporting nature

I encountered an error stating "ERROR Error: Cannot find a differ supporting object '[object Object]' of type 'object'. NgFor only supports binding to Iterables such as Arrays." This is my TypeScript file: this.list = data.json(); ...

Connect data dynamically to the p-table based on columns

I'm facing a challenge in displaying JSON data in a table format, and I'm looking for a way to accomplish this using p-table. When looping through the data, I'm noticing duplicate records in the rows. Can anyone guide me on how to achieve th ...

Transforming date and timezone offset into an isoDate format using moment.js

When retrieving data from the API, I encounter Date, Time, and Offset values in separate columns. My goal is to obtain an ISO date while maintaining the original date and time values. const date = "2019-04-15" const time = "13:45" const ...

Passing the value of an Angular component to a different component

I have a menu in my application that uses IDs to route content, and I also have a detailed view where the content should be displayed based on those same IDs. Currently, I am trying to display objects by their ID when a button is clicked. However, I' ...

Express throwing module errors

I encountered an issue while attempting to expose a REST service in an electron app using expressJS. Following a tutorial, I added express and @types/express to the project. However, when trying to implement a "get" method and running the build with ng bui ...

Display a React Material-UI Button variant depending on the isActive state of a NavLink using TypeScript with Material-UI and React Router v

I'm attempting to dynamically change the variant of a mui Button based on the isActive state of a Navlink, but I'm running into an error <Button to="/" component={NavLink} variant={({isActive}:{isActive:any}) => isActive ? 'contained&a ...

Can someone assist me in creating mongoose models?

Currently, I am focused on managing products and categories These are the two types I have created: type Category = { parent: Category | null; // Is this acceptable? name: String; }; type Product = { categories: Category[]; name: String; ...

Guiding TypeScript to autonomously deduce the precise types of an object that implements a generic interface

Within this code snippet, TypeScript appears to be unaware of the specific type associated with userService.getByUserId in the final line. The expected type should be (userId: string) => ServiceResult<User>, but TypeScript is enforcing a more gene ...

Investigating TypeScript Bugs in Visual Studio Code

As I navigate through various sources, I notice that there is a plethora of information available on older versions of VSCode (v1.16.1 - the most recent version at the time of writing) or deprecated attributes in the launch.json file. I have experimented ...

"Implementing a loop to dynamically add elements in TypeScript

During the loop session, I am able to retrieve data but once outside the loop, I am unable to log it. fetchDetails(){ this.retrieveData().subscribe(data => { console.log(data); this.data = data; for (var k of this.data){ // conso ...

Patience is key as you wait for the observable to finish

My methods have dependencies where one method needs to complete before the next can be called. process1(data: string) : Observable<string> { this.dataservice.process(data).subscribe( (response) => { return response. ...