so this might be a long post, but I hope someone can help me out :)
I am building an Angular 2 web based application with a "split" layout, meaning I will use a separate layout component depending on whether or not I am authenticated via route guard.
When I enter the application for the first time and I have no active session or cookie from the server, I will be redirected to a login page, but only if that page is protected via a routing guard. This login page will be put into a "public" view that contains no <header>
, <footer>
or <nav>
tags in it.
The purpose of this is to be able to have one HTML layout loaded
with a <router-outlet></router-outlet>
for view-components that require a user to be logged in, and have another <router-outlet></router-outlet>
in another view-component when the user is trying to access a "private" route.
Inside index.html
<html>
<head></head>
<body>
<!-- Load application -->
<my-app>
<div class="vertical-wrapper center-block">
<div class="vertical-align">
<i class="fa fa-spinner fa-pulse fa-5x fa-fw"></i>
<span class="sr-only">Loading Application...</span>
</div>
</div>
</my-app>
<!-- Scripts -->
<script src="/dist/app.vendor.js"></script>
<script src="/dist/app.js"></script>
<!-- Development Server Reloading -->
<script src="/webpack-dev-server.js"></script>
</body>
</html>
Routes.ts file
// Required Imports.
import { Routes, RouterModule } from '@angular/router';
import { NgModule } from '@angular/core';
// Routes values
import { PUBLIC_ROUTES } from 'app/routes/public.routes';
import { PRIVATE_ROUTES } from 'app/routes/private.routes';
// Layouts
import { PublicComponent, PrivateComponent } from 'app/layouts';
// Import Pages
import { NotFoundPage } from 'app/pages';
// Guards
import { LoginGuard } from 'app/services/login.guard';
const routes: Routes = [
{
canActivate: [LoginGuard],
path: '',
component: PrivateComponent,
children: PRIVATE_ROUTES
},
{
path: '',
component: PublicComponent,
children: PUBLIC_ROUTES
},
// Greedy-route sending everything not caught to 404 page.
{ path: '**', component: NotFoundPage }
];
@NgModule({
imports: [
RouterModule.forRoot(routes)
],
exports: [RouterModule]
})
export class RoutingModule { }
Then, inside of my private and public components, I put my "child" routes, just to keep my application clean.
Private routes file
import { StartPage, ListComponent } from 'app/pages';
export const PRIVATE_ROUTES = [
{
path: '',
component: StartPage
},
{
path: 'start',
component: StartPage
},
{
path: 'lists',
component: ListComponent
}
];
Public routes file
import { LoginPage, LogoutPage } from 'app/pages';
export const PUBLIC_ROUTES = [
{
path: 'login',
component: LoginPage
},
{
path: 'logout',
component: LogoutPage
}
];
I am loading either the private or public component depending on if the user is trying to access a page that is under a guard or not.
Now to the final part. Inside my "layouts" folder, I have both the private.component.html
and the public.component.html
, both containing a <router-outlet></router-outlet>
.
Private component html
<div id="page-wrapper" [ngClass]="{'toggled': ShowNavigation}">
<div id="navigation-wrapper">
<navigation></navigation>
</div>
<div id="content-wrapper">
<header class="col-xs-12 fixed-height-60" style="border-bottom: 2px solid #92A92A">
<div class="pull-right vertical-wrapper">
<div class="vertical-align padding-right-x1">
<span class="col-sm-12 hidden-xs text-right">{{CurrentUser.userDisplayName}}</span>
<span class="col-sm-12 hidden-xs text-right">{{CurrentUser.clientDescription}}</span>
</div>
<div class="vertical-align image-wrapper">
<div class="col-sm-12 col-xs-9 pull-xs-right">
<div class="row">
<img class="image" [src]="BrandingLogo" alt="Banding logo" />
</div>
</div>
</div>
</div>
</header>
<section class="col-xs-12">
<router-outlet></router-outlet>
</section>
</div>
</div>
Public component html
<div class="container-fluid">
<div class="row">
<router-outlet></router-outlet>
</div>
</div>
My question is, how do I register route childes that should only be accessible if the route guard allows it? I need to be able to switch the layout, because only inside the application itself should you be able to view the <nav>
for example.
I know this is not the best way to do this, but this was how I manage to solve it. Feel free to contribute with anything that can help me improve this.
- Is there any good way to accomplish this?
- Am I on the right track, or have I completely lost it?
I'll provide you with my guard if anyone of you need to see that as well.
import { Inject, Injectable } from '@angular/core';
import { LoginService } from 'app/services/login.service';
import {
CanActivate,
Router,
RouterStateSnapshot,
ActivatedRouteSnapshot
} from '@angular/router';
@Injectable()
export class LoginGuard implements CanActivate {
constructor(
@Inject(LoginService) private loginService: LoginService,
@Inject(Router) private router: Router
) { }
canActivate( activatedRoute: ActivatedRouteSnapshot,
routerState: RouterStateSnapshot ): boolean {
if (this.loginService.User)
return true;
this.router.navigate(['/login', { redirect: routerState.url.substr(1) }]);
return false;
}
}
Here is a owerview of my Source tree
Feel free to ask me anything if I was not clear enough.