Combine a pair of select statements to utilize the RxJS store within an Angular Guard

When working on an Angular Guard, I encountered a challenge where I needed to select two fields from the ngrx store. Here is the code snippet for reference:

@Injectable()
export class RoleGuard implements CanActivate {

  constructor(
    public router: ActivatedRouteSnapshot,
    private store: Store<AppState> ) {}

  canActivate(route: ActivatedRouteSnapshot): Observable<boolean> {

    const expectedRole = route.data.Role;

    return combineLatest(
        this.store.pipe(select(isLoggedIn)),
        this.store.pipe(select(currentUser)),
      ).pipe(
          tap( ([loggedIn, user]) => 
                {
                    if ( loggedIn && !(user.role.find(expectedRole) >= 0) ) {
                        this.router.navigateByUrl('/error/403')
                    };
                }
            )
        );


  }

}

Despite my efforts, I encountered the error

Type 'boolean | [any, any]' is not assignable to type 'boolean'
, which is due to the combineLatest returning the result in an array format. I'm seeking a more elegant solution than using combineLatest and avoiding nesting the select observables. Are there any suggestions for better alternatives?

Answer №1

The canActivate method is expected to return a boolean within an Observable. Originally, your code returns values wrapped in an Observable from the combineLatest method, which results in an array being returned. To address this, you can utilize the map operator to easily return either true or false.

@Injectable()
export class RoleGuard implements CanActivate {
  constructor(
    public router: ActivatedRouteSnapshot,
    private store: Store<AppState> ) {}

  canActivate(route: ActivatedRouteSnapshot): Observable<boolean> {

    const expectedRole = route.data.Role;

    return combineLatest(
        this.store.pipe(select(isLoggedIn)),
        this.store.pipe(select(currentUser)),
      ).pipe(
          map( ([loggedIn, user]) => 
                {
                    if ( loggedIn && !(user.role.find(expectedRole) >= 0) ) {
                      this.router.navigateByUrl('/error/403')
                      //In case you wish for the guard to fail and redirect to the 403 page, then return false
                      return false;
                    };

                  //If the above condition fails and you want to pass the guard, then return true; essentially, adjust the return based on your requirements.
                  return true;

                }
            )
);
  }

}

I trust this provides some clarity.

Answer №2

If you're looking to combine observables, you have a couple of options:

forkJoin([
        this.store.pipe(select(isLoggedIn)),
        this.store.pipe(select(currentUser)),
      ])

Alternatively, you can use the merge operator:

The merge operator creates an output Observable that concurrently emits all values from every given input Observable.

import { merge, interval } from 'rxjs';
import { take } from 'rxjs/operators';

const timer1 = interval(1000).pipe(take(10));
const timer2 = interval(2000).pipe(take(6));
const timer3 = interval(500).pipe(take(10));
const concurrent = 2; // The argument for concurrency
const merged = merge(timer1, timer2, timer3, concurrent);
merged.subscribe(x => console.log(x));

Answer №3

When working with independent services and only interested in the final emitted value of each observable, forkJoin can be a useful tool. However, if one service's response is needed by another service, the code snippet below demonstrates how to handle this scenario.

canActivate(route: ActivatedRouteSnapshot): Observable<boolean> {

return this.store.pipe(select(isLoggedIn)).pipe(switchMap(loggedIn)=>{
     return _checkLoggedIn(loggedIn);
   }),
   catchError(() => {
      return of(false);
    }));
}


private _checkLoggedIn(loggedIn): Observable<boolean> {
  if(loggedIn){
    return of(true);
  } else {
    return this.store.pipe(select(currentUser)).pipe(map((currentUser)=>{
      return currentUser;
    }));
  }
}


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

Angular Redirect Function: An Overview

In the Angular project I'm working on, there is a function that should navigate to the home when executed. Within this function, there is a condition where if true, it should redirect somewhere. if (condition) { location.url('/home') ...

Moving DOM Elements around

Objective: Looking to relocate an element within the DOM structure. Condition: Although I am familiar with using jQuery for this task, I am currently working in an environment without access to it. Therefore, I need a pure JavaScript solution. Example: ...

Establish the directive upon receiving the broadcast

Is there a way to trigger a directive when a specific event occurs on the backend and sets a value to false?... This is where the event is being captured .factory('AuthInterceptor', function($q, $injector, $rootScope) { return { ...

I am looking to utilize the JavaScript YouTube API to seamlessly upload a video from my website directly to YouTube

Currently facing an issue with uploading a video from my webpage to YouTube using the JavaScript YouTube API. The error code I'm receiving is "User authentication required" (401). Can anyone provide me with a demonstration example in JavaScript that s ...

The constructor error in ng-serve signalizes an issue in

Currently, I am developing an AngularJS application. However, when attempting to start the dev server, I encountered an issue with my ng serve command: https://i.stack.imgur.com/QujSe.png ...

AngularJS not displaying loader during AJAX request

While utilizing ajax requests with $http, there seems to be a delay due to the server operation taking longer than expected. I have implemented a loader to display while processing the request, but unfortunately it is not showing up on the page. Even after ...

Leveraging the power of the three.js library on the client-side within a vue.js/n

I'm facing a challenge with incorporating the three.js library (installed via npm) to display 3D models on the client side within my nuxt.js application. Despite multiple attempts, I seem to be hitting a roadblock with the import not functioning prope ...

Refresh a dynamically loaded webpage following an update

Trying to grasp the concept of reloading a dynamic page loaded with AJAX after a record is updated. Here's the jquery script on the page: <script type="text/javascript> function showUser(str) { if (str == "") { $("#txtHint").empty() ...

Troubleshooting: AngularJS ng-repeat not rendering JSON data

I have successfully retrieved JSON data from a database using PDO in Angular. The data is being returned as expected when I encode it to JSON. However, I am facing an issue with displaying the data using ng-repeat in Angular. Although the div elements are ...

Display custom modals in React that showcase content for a brief moment before the page refreshes

I recently developed a custom modal in React. When the Open button is clicked, showModal is set to true and the modal display changes to block. Conversely, clicking the Close button sets the display to none. However, I noticed a bug where upon refreshing ...

Guide on triggering a modal upon receiving a function response in React

I am looking for a way to trigger a function call within a response so that I can open a modal. Currently, I am utilizing Material UI for the modal functionality. Learn more about Modals here The process involves: Clicking on a button Sending a request ...

Unable to retrieve a substring value in Angular using Typescript

html <p> <input type="text" maxlength="40" (input)="recipientReference = deleteSpacing(recipientReference)" [(ngModel)]="recipientReference" style="width: 30vw; padding: 5px;border: 1px solid;border ...

Issue: friends.js file contains an unexpected token "<" error after implementing express.static and modifying HTML content upon button click

On localhost:9000, I'm attempting to load home.html initially. However, when I try it with my current code, I encounter the error: friends.js:1 Uncaught SyntaxError: Unexpected token <. I'm unsure about the meaning of this error. Additionally, ...

Achieving camera zoom in threeJS without the use of trackball controls or any other camera control libraries

Currently, I'm utilizing threeJS to manipulate a camera within my scene. The camera is configured to orbit in a circular motion around an object when the left and right keys are pressed on the keyboard. However, I am seeking guidance on how to impleme ...

convert JSON to Java object using GSON with a map

Here is the structure of my Java POJO: public class MyPersonTO{ String name; String surname; Map<String, Double> categories; } Currently, I am using the Gson library, but I'm uncertain about the formatting of my JSON string and the obje ...

I keep getting redirected to a blank page by JS

I've created a JavaScript script that smoothly fades in the page when a user enters it and fades out when they click a link to another page. The script is working as intended, but I'm facing an issue with anchor links on the page. Whenever I clic ...

Incorporating a new data series into your candlestick chart with Highstock

I've encountered an issue with adding a data series to my candlestick chart in Highstock using Highcharts. Here's the script I'm using: $.ajax({ url : 'indicator', type : 'GET', data ...

Storing AngularJS route components in the cache for optimal performance (keep-alive)

I'm looking for a way to cache the state of a component A so that it doesn't re-render every time I navigate away and back to it. This component also makes a slow API call in its constructor. I want to maintain this initial state throughout the u ...

Attention: It is not possible to update the React state on a component that is not mounted. Please use the useEffect cleanup function

Can you assist with solving this issue? https://i.sstatic.net/yKFzr.png //ReviewProductDetail.jsx : Initially, the page was populated from this particular component import React, { useEffect, useState } from 'react'; import RatingCardProductD ...

Problem with JavaScript and Basic HTML5 Canvas

I'm trying to dive into learning about using a canvas, but I just can't seem to get this basic code to work. Does anyone know what I might be doing wrong? Link to jsfiddle <canvas id="ctx" width="500" height="500" style="border:1px solid #00 ...