Alex
Alex

Reputation: 1644

Achieve homepage with two different possible routes

google.com is the homepage. I need two different routes for the homepage. The first route works while the user is not logged in (Auth) and the second route works while the user is logged in (Dashboard).

The hard part is when both auth and dashboard pages work on a single homepage, not different URLs.

Note: Both modules have multiple components. Auth has (login - signup - reset) components and Dashboard has (index - users - posts) components.

const routes: Routes = [
  {
    path: 'index',
    loadChildren: './modules/index/index.module#IndexModule'
  },
  {
    path: 'error',
    loadChildren: './modules/error/error.module#ErrorModule'
  },

  // Fallback when no prior routes is matched
  { path: '**', redirectTo: 'not-found', pathMatch: 'full' }
];

@NgModule({
  imports: [RouterModule.forRoot(routes, { useHash: false })],
  exports: [RouterModule],
  providers: []
})
export class AppRoutingModule { }
const routes: Routes = [
  {
    path: '',
    children: [
      {
        path: '',
        loadChildren: './modules/home/home.module#HomeModule',
        canLoad: [AuthGuard],
      },
      {
        path: 'auth',
        loadChildren: './modules/auth/auth.module#AuthModule',
        canLoad: [NoAuthGuard]
      },
    ]
  }
];

@NgModule({
  imports: [RouterModule.forChild(routes)],
  exports: [RouterModule]
})
export class IndexRoutingModule { }
const routes: Routes = [
    {
        path: '',
        component: HomeLayoutComponent,
        pathMatch: 'full',
        children: [
            {
                path: '',
                redirectTo: 'dashboard',
                pathMatch: 'full'
            },
            {
                path: 'dashboard',
                component: DashboardComponent,
                data: { title: 'Dashboard' }
            },
        ]
    },
];

@NgModule({
    imports: [RouterModule.forChild(routes)],
    exports: [RouterModule]
})
export class HomeRoutingModule { }
const routes: Routes = [
    {
        path: '',
        component: AuthLayoutComponent,
        pathMatch: 'full',
        children: [
            {
                path: '',
                redirectTo: 'login',
                pathMatch: 'full'
            },
            {
                path: 'login',
                component: LoginComponent,
                data: { title: 'Sign In To Admin' }
            },
        ]
    }
];

@NgModule({
    imports: [
        RouterModule.forChild(routes)
    ],
    exports: [
        RouterModule
    ]
})
export class AuthRoutingModule { }

The problem is only with routing, not guards and not anything.

Upvotes: 2

Views: 165

Answers (2)

orelby
orelby

Reputation: 367

There's a detailed answer on another question about matching different routes with the same URL depending on an asynchronous condition.

In short, in Angular 14.1+ use CanMatch guards. In fact, CanLoad is planned to be deprecated in Angular 15.1 in favor of CanMatch.

There is an issue with using CanLoad for this purpose in the first place: you would also need CanActivate, since CanLoad only runs once (if the child routes were loaded it will not run again).

Now with CanActivate added, it does not run before the routes' matchers, so the previous answer for this question is incorrect. Angular first determines the route (the entire route path from the parent to the innermost child), and only then runs CanActivate guards.

For the approach from that answer to work (using matchers with a variable that gets updated in the CanActivate guard), you could redirect in canActivate if the new value is different, to trigger another route matching with the updated auth state. You'd also need to make sure both of those routes match the other's sub routes (or add a wildcard child route), or there will be cases where the current auth state will cause no child route to be matched, and canActivate will not run.

One last thing is replacing CanActivate with CanActivateChild.

Upvotes: 0

Reactgular
Reactgular

Reputation: 54811

The first route works while the user is not logged in (Auth) and the second route works while the user is logged in (Dashboard).

It's tricky because the user's state is difficult to access from the router configuration.

I recommend you use a UrlMatcher to control which route configuration is used and when it is used.

https://angular.io/api/router/UrlMatcher

The tricky part is accessing the current user's logged in state, and I assume this state is only known by a service. Something like UsersService.isLogged() that returns an observable of true/false, because maybe it needs to contact the server to restore a previous session.

So what I would do is used an activator to read this state into a global variable, and then use that variable from the UrlMatcher. The activator doesn't do any logical control of the route, because it will always yield a true to activate the route. We just want to gain access to a service and perform an async operation.

If you define an empty parent route that uses an activator, then I assume that Angular will resolve the activator before processing child routes.

let globalUserState = false;

class UserActivator implements CanActivate {
    public constructor(private users: UserService) {
    }

    public canActivate(): Observable<boolean> {
        return this.users.isLoggedIn().pipe(
            tap(value => globalUserState = value),
            mapTo(true)
        );
    }
}

const routes: Routes = [
    {
        path: '',
        canActivate: [UserActivator],
        children: [
            {
                // you can define more than one child using different matching rules
                matcher: (url: UrlSegment[]) => {
                    return url.length === 1 && url[0].path === '/' && globalUserState ? ({consumed: url}) : null;
                },
                children: [
                    {
                        // lazy modules must be loaded as children
                        path: '',
                        loadChildren: '../lazy/home/home.module#HomeModule'
                    }
                ]
            },
        ]
    }
];

Upvotes: 1

Related Questions