2

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.

krilllind
  • 53
  • 1
  • 6
  • Any way you could put together a plunker of the basic parts of this so we can give it a try? – DeborahK May 02 '17 at 15:21
  • What about adding canActivateChildren? – yoonjesung May 02 '17 at 18:46
  • @DeborahK There is no easy way for me to create a plunker example of this. It's just so many parts that needs to be put together in order for me to get it working the same way... I can try but it might take awhile. – krilllind May 03 '17 at 05:44
  • @yoonjesung Do you think you could maybe give me an example of how to apply that for this solution? – krilllind May 03 '17 at 05:45
  • @krilllind It's the same as canActivate: [LoginGuard] except you use canActivateChild: [LoginGuard] instead. canActivate works "top-down" whereas canActivateChild works "bottom-up". Here is another post with some answers on the topic of canActivate vs. canActivateChild: http://stackoverflow.com/questions/40163348/canactivate-vs-canactivatechild-with-component-less-routes – yoonjesung May 03 '17 at 16:17

0 Answers0