What is the best way to implement an Angular Guard that utilizes an API service for validation and redirects in case of failure?

Hello there!

I am currently working on an Angular 7 application that deals with time cards. One of the main features I have implemented is a CanActivate Guard for controlling access to certain components. The CanActivate code utilizes Observables to decide whether a user should be allowed entry or redirected.

  timecardID: number;
  constructor(private globals: Globals, private router: Router, private securityService: SecurityService) { }
  canActivate(
    next: ActivatedRouteSnapshot,
    state: RouterStateSnapshot): Observable<boolean | UrlTree> | boolean {

    if (next.params.timecardId) {
      this.timecardID = +next.params.timecardId;
      return this.hasAccessToTimecard();
    }

    return this.empNumber === this.globals.empNumber;
  }


  //Checking if current user has access to specific timecard
  hasAccessToTimecard(): Observable<boolean | UrlTree> {
    return this.securityService.hasRightsToTimecard(this.timecardID);
  }

The following snippet shows the service being called:

  private readonly HasRightsToTimecardURI = 'api/Security/HasRightsToTimecard';
  private readonly HasRightsToEmployeeURI = 'api/Security/HasRightsToEmployee';

  constructor(private http: HttpClient, @Inject('BASE_URL') private baseUrl: string, private router: Router) { }

  //I don't like the use of router to navigate here. But without it, on false return the UrlTree isn't used for redirections.
  public hasRightsToTimecard(timecardID: number): Observable<boolean | UrlTree> {
    let parameters = new HttpParams();
    parameters = parameters.set('timecardID', String(timecardID));

    return this.http.get<boolean | UrlTree>(this.baseUrl + this.HasRightsToTimecardURI, { params: parameters }).pipe(
      tap((result) => {
        this.router.navigate(['/home']);
        return  result ? result : this.router.createUrlTree(['/home']);
      })
    );
  }

I have confirmed that the logic works correctly, creating a UrlTree when the API returns false and handling true results properly.

However, I am facing issues with accessing the route through different methods:

  1. When using controls within the app's component, I have proper access as the guard allows me through
  2. If incorrect access is detected using controls in the component, the guard prevents me from proceeding to the route, which is expected behavior
  3. Directly typing the route URL in the browser results in a blank screen, instead of redirecting or showing content

If anyone could provide assistance, I would greatly appreciate it.

In addition, here's a relevant part of the route setup (excluding other details):

const routes: Routes = [
  {
    path: 'timecards', canActivateChild: [RightsGuard], children: [
      { path: 'edit/:timecardId', canActivate: [TimecardGuard], component: TimecardNewComponent }
    ]
  }
];

Answer №1

After spending some time working on it, I finally figured out the solution to my problem. It turns out that I was using tap() incorrectly. According to the documentation, tap does not change the value; it always returns the original one. In this case, map() was the correct function to use.

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

What is the process for invoking an External Javascript Firestore function within a Typescript file?

Trying to figure out how to integrate a Firestore trigger written in an external JavaScript file (notifyNewMessage.js) into my TypeScript file (index.ts) using Node.js for Cloud functions. Both files are located in the same directory: https://i.stack.imgu ...

Creating a fresh ngx-translate pipeline (comparing pure and impure methods)

Edit: I am looking to enhance the functionality of ngx-translate's pipe by extending it. Here is an example of how I achieved this: import { Pipe, PipeTransform } from '@angular/core'; import { TranslatePipe } from "@ngx-translate/core"; @ ...

How is it possible for this for loop to function properly without the need to pass the incrementing variable

I managed to compile my code and it's working fine, but there's something interesting - the variable that should reference the incrementing value is not included as an argument in the for loop. var _loop2 = function _loop2() { var p = docume ...

The issue with Angular version 15 p-dialogue not displaying HTML content when using a component selector

In my Angular application, I have an issue with rendering a component called urc.component from a different module (variance.module) inside another module (nursing-audit.module). The p-dialogue is opening and displaying the header correctly, but the urc.co ...

Issue encountered while generating a fresh migration in TypeORM with NestJs utilizing Typescript

I am currently working on a Node application using TypeScript and I am attempting to create a new migration following the instructions provided by TypeORM. Initially, I installed the CLI, configured my connection options as outlined here. However, when I ...

Setting the initial navigation theme based on route parameters from an external source, not within the StackNavigator

Is there a way to set the initial navigation theme based on the current route params without rendering the NavigationContainer and causing a flash of content with the default theme? Can the route be accessed from outside of the NavigationContainer without ...

What steps can be taken to choose an Angular npm package for datetimepicker in a TypeScript file?

I am facing an issue where document.getElementById and document.querySelector are not returning any results ... I need to access this specific HTML element in order to set a new attribute at a certain event. <datetime id="datePicker" [datepicker]="da ...

Having trouble retrieving files from an Angular2 service

I am facing an issue in creating an Angular2 service for downloading files from the server. I have a table where each record represents a single file. When clicking on a specific record, the download method is called: download(r: FileObject) { this.re ...

Interactive form control for location details including country, state, district, and town

I am struggling with adding dynamic form controls on dropdown change. I have been able to add them, but encountered an error preventing me from retrieving the value in 'formName.value'. The specific error message states: "Error: There is no Form ...

Upon the second click, the addEventListener function is triggered

When using the window.addEventListener, I am encountering an issue where it only triggers on the second click. This is happening after I initially click on the li element to view the task information, and then click on the delete button which fires the eve ...

You must add the module-alias/register to each file in order to use path aliases in

I am currently utilizing typescript v3.6.4 and have the following snippet in my tsconfig.json: "compilerOptions": { "moduleResolution": "node", "baseUrl": "./src", "paths": { "@config/*": ["config/*"], "@config": ["config"], ...

Lazy loaded modules do not have access to singleton services

After breaking my initial AppModule into multiple modules, I decided to lazy-load one of the modules while using singleton services from a shared module. Following the instructions provided in the official Angular documentation (link here) and in a tutori ...

Tips for utilizing the forEach method in Angular 2 without relying on ngFor?

I recently started learning Angular 2 and I am trying to figure out how to access array details using a forEach loop and apply certain conditions on it. Once I make the necessary changes, I want to display this data using ngFor. In Angular 1, this was ea ...

Exploring the usage of array map parameters in rxjs 6 when combined with withLatestFrom

Prior to Rxjs 6, we were able to achieve the following: interface TypeA { payload: any; } source$.pipe( withLatestFrom(source2$, (source1: TypeA, source2: TypeB) => ({ payload: source1.payload, source2 }) ), ) In the resultSelector method ...

Optimal practices for checking the status of your request

In my Node.js backend, I used to include a boolean value in my response to indicate successful operations: if(req.body.user.username == null || req.body.user.username == '' || req.body.user.password == null || req.body.user.password == '&ap ...

Error: The object is not defined (evaluating '_$$_REQUIRE(_dependencyMap[32], "react-native-safe-area-context").SafeAreaView')

I am currently working on developing a chat application using react-native with the following dependencies: "dependencies": { "@react-native-async-storage/async-storage": "~1.17.3", "@react-native-community/masked ...

What is the best way to utilize angular2 components based on their name using ngFor?

Check out the components I've created: app-widget-resume app-widget-my-courses-learner app-widget-my-courses-teacher app-widget-my-calendar app-widget-virtual-classes I want to populate my dashboard component, called my-dashboard, by util ...

What is the rationale behind TypeScript's decision to permit omission of "this" in a method?

The TypeScript code below compiles without errors: class Something { name: string; constructor() { name = "test"; } } Although this code compiles successfully, it mistakenly assumes that the `name` variable exists. However, when co ...

Components in Ionic used in app.component.ts and various pages

Struggling with integrating a custom component in Ionic. In my app.html, I have a menu and I'm using lazy-loading for the pages. I am trying to include the component in both the menu in app.html and on some pages. However, I'm facing an issue ...

Setting up NestJs with TypeORM by utilizing environment files

In my setup, I have two different .env files named dev.env and staging.env. My database ORM is typeorm. I am seeking guidance on how to configure typeorm to read the appropriate config file whenever I launch the application. Currently, I am encountering ...