Daniel Grima
Daniel Grima

Reputation: 2825

Angular 2 hiding navigation on certain pages based on route

I'm trying to automatically hide the navigation when the user is viewing certain pages. For example if the user is viewing the login page the menu should not be displayed.

What I've tried so far is that I have a Navigator service that will change a "show navigation" flag based on the URL path of the current page. This is accessed by an App Guard whose job so far is to pass the next URL to the Navigator service and update the "show navigation" flag.

So far as a test I'm updating the view via a click event to check that the service is receiving the correct data and updating accordingly.

The only issue I have is that I can't think of a way to subscribe/listen to the changes of the "show navigation" flag "all the time". What I mean is that I'd like the navigation to be hidden and shown automatically.

Navigator Service

import { Injectable } from '@angular/core';

import { Observable } from 'rxjs/Observable';

@Injectable()
export class NavigatorService {
    private showNavigation: boolean = false;

    displayNavigation(): Observable<boolean> {
        return Observable.of(this.showNavigation);
    }

    toggleNavigation(urlPath: string) {
        if (urlPath === 'login') {
            this.showNavigation = false;
        } else {
            this.showNavigation = true;
        }
    }
}

App Guard

import { Injectable } from '@angular/core';
import { ActivatedRouteSnapshot, CanActivate } from '@angular/router';

import { Observable } from 'rxjs/Observable';

import { NavigatorService } from '../services';

@Injectable()
export class AppGuard implements CanActivate {
    constructor(private navigatorService: NavigatorService) { }

    canActivate(next: ActivatedRouteSnapshot) {
        console.log(next);

        this.navigatorService.toggleNavigation(next.url[0].path);

        // for now returning Observable of true...
        // will be changed when extra functionality is added
        return Observable.of(true);
    }
}

App Component

import { Component } from '@angular/core';

import { NavigatorService } from './shared';


@Component({
    selector: 'app',
    styleUrls: [
        './app.style.css'
    ],
    template: require('./app.component.html')
})
export class AppComponent {
    showNavigation: boolean = true;

    constructor(private navigatorService: NavigatorService) { }

    // button click event
    toggleNav() {
        this.navigatorService.displayNavigation
            .subscribe(res => this.showNavigation = res);
    }
}

App Component Template

<button (click)="toggleNav()">Toggle Navigation</button>
<div id="page-wrapper">
    <div id="sidebar-wrapper" *ngIf="showNavigation">
        <navigation></navigation>
    </div>
    <div id="page-content-wrapper">
        <main>
            <router-outlet></router-outlet>
        </main>
    </div>
</div>

As can be seen above for now as a test I have a "Toggle Navigation" button so that I can subscribe to the service. To be honest I don't know if this is the best implementation and maybe I'm not going about it in the correct way. I'd like to ask if there's a way that somehow the App Component would always be aware of the "show navigation" flag's value.

Update - Issue resolved

I've followed this question which correctly lead me to the answer. I updated the Navigation service as follows:

Navigator Service

import { Injectable } from '@angular/core';

import { Observable } from 'rxjs/Observable';
import { Subject } from 'rxjs/Subject';

@Injectable()
export class NavigatorService {
    private showNavigation: Subject<boolean>;

    constructor() {
        this.showNavigation = <Subject<boolean>>new Subject();
    }

    get displayNavigation(): Observable<boolean> {
        return this.showNavigation.asObservable();
    }

    toggleNavigation(urlPath: string) {
        let showNav: boolean;
        if (urlPath === 'login') {
            showNav = false;
        } else {
            showNav = true;
        }

        this.showNavigation.next(showNav);
    }
}

And then I subscribe to the service in the App Component's ngOnInit lifecycle hook:

import { Component, OnInit } from '@angular/core';

import { NavigatorService } from './shared';

@Component({
    selector: 'app',
    styleUrls: [
        './app.style.css'
    ],
    template: require('./app.component.html')
})
export class AppComponent implements OnInit {
    showNavigation: boolean = true;

    constructor(private navigatorService: NavigatorService) { }

    ngOnInit() {
        this.navigatorService.displayNavigation
            .subscribe(res => this.showNavigation = res);
    }
}

I'm still not 100% sure if this is the best approach but it's resolved my issue so far.

Upvotes: 4

Views: 6641

Answers (1)

kmac.mcfarlane
kmac.mcfarlane

Reputation: 307

For others' reference, you can do this just with the router. I have an example below in which I have just a <router-outlet></router-outlet> in the AppComponent's template, and move all the stuff you want hidden to a separate layout view called DefaultLayoutComponent.

Put your menus/footer and a <router-outlet></router-outlet> in DefaultLayoutComponent. Now only children of DefaultLayoutComponent (i.e. dashboard and invite components below) will have the common layout. Other pages (i.e. login below) will have a blank layout.

RouterModule.forRoot([
  { path: 'login', component: LoginComponent},
    { path: '', component: DefaultLayoutComponent, children: [
      { path: '', component: DashboardComponent},
      { path: 'dashboard', component: DashboardComponent },
      { path: 'invite', component: InviteComponent}]
    },
  { path: '**', redirectTo: ''}
])

Upvotes: 2

Related Questions