Search code examples
angularrxjsangular-ui-routerobservableangular-router-guards

Router does not navigate to targeted component


I have a function that redirects a user to a page Orders if the user's state is complete, and to a HOME page if the user's state is not complete.

The Orders page has a GuardService that implements canActivate, and the HOME page does not have any guards.

I have some users with the R_ORDERS right and in a complete state who are not navigating at all. (ie they are neither routed to the Orders page nor the HOME page).

I can see that the HTTP requests retrieving the user's right is called but they are stuck in that page.

Is my issue related to this post ? enter link description here

or to this article enter link description here

Also, I have tried to add logs when the observable has completed but in that case, the log is never displayed in the console.

return this.getList().subscribe((e) => {
        const right = new List(e).FirstOrDefault(
          (d) => d.code == securizedPath.keyCode
        )?.type;
        if (!right) return resolve("OFF");
        return resolve(right);
      },
        (error) => {
          console.log("Right store threw an error");
          console.log(error);
        },
        () => {
          console.log("Right list completed")
        }
);

thanks for your help

async redirectTo() {
  this.loading = true;
  this.userState = await this.stateService.getToPromise(this.userId);


  if (this.userState.isComplete) {
     this.orderService.init();
     this.router.navigate([RouteNames.ORDERS]);
     this.loading = false;
     return;
  }

  this.router.navigate([RouteNames.HOME]);
  return;
  
}

The RouteNames.ORDERS has the following guard:

@Injectable({
  providedIn: "root",
})
export class GuardService implements CanActivate {
  constructor(
    private router: Router,
    private rightService: UserRightService
  ) {}
  canActivate(
    route: import("@angular/router").ActivatedRouteSnapshot,
    state: import("@angular/router").RouterStateSnapshot
  ):
    | boolean
    | import("@angular/router").UrlTree
    | import("rxjs").Observable<boolean | import("@angular/router").UrlTree>
    | Promise<boolean | import("@angular/router").UrlTree> {
    return this.test(route);
  }
  async test(route: ActivatedRouteSnapshot) {
    const right = await this.rightService.isRouteAllowed(route.url[0].path);
    if (right == "OFF") {
      this.router.navigate([RouteNames.FEATURE_UNAUTHORIZED]);
      return false;
    } else if (right != "ON") {
      this.router.navigate([RouteNames.FEATURE_UNAUTHORIZED]);
      return false;
    }
    return true;
  }


}

Code for the UserRightService is

 isRouteAllowed(route: string): Promise<Right> {

    const securizedPath = new List(this.securizedPaths).FirstOrDefault(
      (e) => e.name == route
    );
    return new Promise((resolve) => {
      return this.getList().subscribe((e) => {
        const right = new List(e).FirstOrDefault(
          (d) => d.code == securizedPath.keyCode
        )?.type;
        if (!right) return resolve("OFF");
        return resolve(right);
      });
    });
  }

securizedPaths is defined as below typescript[{ name: RouteNames.ORDERS, keyCode: "R_ORDERS" }, // other value ]

and getList is

 getList() {
    this.subjectList = new ReplaySubject<T[]>();
    this.load();
    return this.subjectList;
 }

protected load() {
      return this.getService()
        .getAll()
        .then((e) => {
          this.subjectList.next(e);
        })
        .catch((e) => {
          this.subjectList.next([]);
          this.subjectList = null;
        });
  }

// Code for this.getService().getAll()
  async getAll(id?: any, parameter?: any): Promise<Array<T>> {
    let urlParameters;
    ({ urlParameters, parameter } = this.createParameters(parameter, id));
    return this.http
      .get(this.url + (urlParameters ? "?" + urlParameters : ""))
      .toPromise<Array<T>>();
  }

Solution

  • I am NOT* sure whether i understand your problem completely but based on reading i see something which i would like to propose you. I see some issue in GuardService

    @Injectable({
        providedIn: "root",
    })
    
    export class GuardService implements CanActivate {
        constructor(
            private router: Router,
            private rightService: UserRightService
        ) { }
        
        canActivate(
            route: import("@angular/router").ActivatedRouteSnapshot,
            state: import("@angular/router").RouterStateSnapshot
        ): Promise<boolean> {
            
            let resolver: (value: boolean | PromiseLike<boolean>) => void;
    
            const promise = new Promise<boolean>((resolve) => {
                resolver = resolve;
            });
            this.test(route, resolver);
            return resolver;
        }
    
        async test(route: ActivatedRouteSnapshot, resolver: (value: boolean | PromiseLike<boolean>)) {
            const right = await this.rightService.isRouteAllowed(route.url[0].path);
            if (right == "OFF") {
                this.router.navigate([RouteNames.FEATURE_UNAUTHORIZED]);
                return resolver(false);
            } else if (right != "ON") {
                this.router.navigate([RouteNames.FEATURE_UNAUTHORIZED]);
                return resolver(false);
            }
            return resolver(true);
        }
    }
    

    See code I have highlighted in bold.

    I guess you must return a promise which will resolved in test method based on what value will be received in response.

    My proposal is totally based some assumption which helped me to fix an issue in past where CanActivte method of the Gurad wanted to return a value asynchronously.