Search code examples
angularangular-routingangular-router

How to set user-specific routes in Angular?


I have an Angular 6 app that uses provider information to determine the functionality (channels) of the app. I am working with an object that I receive from a server after a user logs on and that provides me with boolean values for the respective channels.

providerInfos = {
  channels: {
    a: true,
    b: true,
    c: true
  }
}

In the index.html I have

<base href="/home/"> 

and in my app-routing.module I have

const routes: Routes = [
  { path: '', redirectTo: 'a', pathMatch: 'full' }, // Set /a as "start page".
  { path: 'a', component: AComponent, runGuardsAndResolvers: 'always' },
  { path: 'a/:id', component: ADetailsComponent },
  { path: 'b', component: BComponent },
  { path: 'c', component: CComponent }
];

So when the app is initialized, A is loaded (somewhere.io/home/a). This was good so far, but due to a further development of the app it turned out that A can't be given as a channel either. This has created a problem for me which I have not been able to solve yet. The more I think about it and look at my code, the more I think that my previous approach is basically wrong.

When initializing the app I want to be able to display B or C as the first or only view (somewhere.io/home/whatever), if A is not part of the app's functionality. In principle I want to provide the app with routes according to the functionality defined for a user via his provider information, at least with

path: '', redirectTo: 'whatever', pathMatch: 'full'

if only one functionality is given.

I can customize the functionality via the provider information, so that the menu in the sidebar is customized and I make the components that are not part of the functionality inaccessible via *ngIf, if someone gets the idea to enter paths in the URL bar. But I can't manage to adjust the routes accordingly.

How would you deal with this problem. Is there best practice for this?


Solution

  • You can use a route guard to achieve this functionality. Not only do route guards prevent a user from accessing a route, they also allow you to tell the router where to navigate to by returning a UrlTree.

    Given the following routes:

    const routes = [
      { path: '', redirectTo: 'a', pathMatch: 'full' },
      { path: 'a', component: ComponentA, canActivate: [AuthGuard] },
      { path: 'b', component: ComponentB }
    ];
    

    I can set up my AuthGuard as follows:

    @Injectable({
      providedIn: 'root'
    })
    export class AuthGuard implements CanActivate {
      constructor(private router: Router, private userService: UserService) {
      }
    
      canActivate() : boolean | UrlTree {
        const user = this.userService.getCurrentUser();
    
        const userCanAccessA = false; // TODO: implement
        if (userCanAccessA) {
          return true;
        }
    
        // redirect to /b
        return this.router.parseUrl('/b');
      }
    }
    

    I am not recommending this example as a good implementation, I am merely demonstrating the capabilities of a route guard. Your requirements will determing how your routes are structured, and what the redirect logic is.

    It can be tempting to make guards do a lot of work. Instead, I would keep each guard focussed on performing a specific task. You can then compose guard logic by combining guards if required.

    Note that in my demo below, component A is never constructed, since AuthGuard prevents access before routing occurs.

    DEMO: https://stackblitz.com/edit/angular-gxsb3m