Home > Enterprise >  Angular RouteGuard "A function whose declared type is neither 'void' nor 'any�
Angular RouteGuard "A function whose declared type is neither 'void' nor 'any�

Time:10-14

I have a route guard that checks if a user has access to a resource, before proceeding. After the subscription, I check if the user has access, and if not, redirect using parseUrl or return true.

@Injectable({ providedIn: 'root' })
export class PortfolioGuard implements CanActivate {
  constructor(private resourceAccessService: ResourceAccessService, private router: Router) { }

  canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<boolean> | Promise<boolean> | boolean | UrlTree {
    const id =  route.paramMap.get('id');
    // return this.resourceAccessService.checkUserAccessToPortfolio(id);
    this.resourceAccessService.checkUserAccessToPortfolio(id).subscribe((hasAccess) => {
      if (!hasAccess) {
        console.log('No access');
        return this.router.parseUrl('/');
      } else {
        console.log('access');
        return true;
      }
    });
  }
}

This works if I only return the check, but I also want it to redirect based on that check.

@Injectable({ providedIn: 'root' })
export class PortfolioGuard implements CanActivate {
  constructor(private resourceAccessService: ResourceAccessService, private router: Router) { }

  canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<boolean> | Promise<boolean> | boolean {
    const id =  route.paramMap.get('id');
    return this.resourceAccessService.checkUserAccessToPortfolio(id);
  }
}

What am I missing in the first block of code, should I be resolving the subscription from the service...

checkUserAccessToPortfolio(id: number): Observable<boolean>  {
    const url = this.baseUrl   this.userResourceAccessUrl   '/'     id   '/portfolio';
    return this.http.get<boolean>(url);
  }

CodePudding user response:

I would try to return the Observable in the function:

canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<boolean> | Promise<boolean> | boolean | UrlTree {
  const id =  route.paramMap.get('id');
    
  return this.resourceAccessService.checkUserAccessToPortfolio(id).pipe(
    map(hasAccess => {
      if (!hasAccess) {
        console.log('No access');
        this.router.parseUrl('/');
        return false
      } else {
        console.log('access');
        return true;
      }
    })
  )
}

CodePudding user response:

The is issue is very obviously. You did not return anything in your function.

Solution:

  1. add return to your function canActivate
  2. remove subscribe and move all logic to map operator. Or you can use tap, switchMap operator. it depends on your context.

Instead of doing this:

@Injectable({ providedIn: 'root' })
export class PortfolioGuard implements CanActivate {
  constructor(private resourceAccessService: ResourceAccessService, private router: Router) { }

  canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<boolean> | Promise<boolean> | boolean | UrlTree {
    const id =  route.paramMap.get('id');
    // return this.resourceAccessService.checkUserAccessToPortfolio(id);
    this.resourceAccessService.checkUserAccessToPortfolio(id).subscribe((hasAccess) => {
      if (!hasAccess) {
        console.log('No access');
        return this.router.parseUrl('/');
      } else {
        console.log('access');
        return true;
      }
    });
  }
}

You can try using pipe and map operator.

@Injectable({ providedIn: 'root' })
export class PortfolioGuard implements CanActivate {
  constructor(private resourceAccessService: ResourceAccessService, private router: Router) { }

  canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<boolean> | Promise<boolean> | boolean | UrlTree {
    const id =  route.paramMap.get('id');
    return this.resourceAccessService.checkUserAccessToPortfolio(id).pipe( map( hasAccess => {
      if(!hasAccess) {
        console.log('No access');
        this.router.parseUrl('/');
        return false
      } else {
        console.log('access');
        return true;
      }
    });
  }
}

  • Related