34

I am created nav bar separately in nav.component.html ,how to hide nav bar in some components like login.component.

nav.component.html

<nav class="navbar navbar-default navbar-fixed-top navClass">
    <div class="container-fluid">
        <div class="navbar-header">
                <button type="button" class="navbar-toggle collapsed"
                        (click)="toggleState()">
                <span class="sr-only">Toggle navigation</span>
                <span class="icon-bar"></span>
                <span class="icon-bar"></span>
                <span class="icon-bar"></span>
            </button>

        </div>
         <div class="collapse navbar-collapse"
              [ngClass]="{ 'in': isIn }">
          enter code here   <ul class="nav navbar-nav">
               <li class="active"><a href="#">Home</a></li>
               <li><a href="#">about</a></li>

            </ul>

        </div>
    </div>
</nav>
User 123
  • 393
  • 1
  • 4
  • 14

9 Answers9

89

Navbar control and formatting is often needed throughout an app, so a NavbarService is useful. Inject in those components where you need.

navbar.service.ts:

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

@Injectable()
export class NavbarService {
  visible: boolean;

  constructor() { this.visible = false; }

  hide() { this.visible = false; }

  show() { this.visible = true; }

  toggle() { this.visible = !this.visible; }

  doSomethingElseUseful() { }

  ...
}

navbar.component.ts:

import { Component } from '@angular/core';
import { NavbarService } from './navbar.service';

@Component({
  moduleId: module.id,
  selector: 'sd-navbar',
  templateUrl: 'navbar.component.html'
})

export class NavbarComponent {

  constructor( public nav: NavbarService ) {}
}

navbar.component.html:

<nav *ngIf="nav.visible">
 ...
</nav>

example.component.ts:

import { Component, OnInit } from '@angular/core';
import { NavbarService } from './navbar.service';

@Component({
})
export class ExampleComponent implements OnInit {

  constructor( public nav: NavbarService ) {}
}
ngOnInit() {
  this.nav.show();
  this.nav.doSomethingElseUseful();
}
Dan
  • 1,355
  • 10
  • 7
  • 1
    Great, glad to help! – Dan Mar 31 '17 at 13:59
  • Thanks for this! I added a possible extension to this answer in my answer below. – trees_are_great Jul 14 '17 at 08:37
  • Thanks for providing the neat solution @Dan – rohit12sh Apr 12 '18 at 17:25
  • I did this way and I ran into 'ExpressionChangedAfterItHasBeenCheckedError'. Any ideas about how can I fix this? – Diogo Moreira Apr 28 '18 at 11:14
  • I'm getting `ExpressionChangedAfterItHasBeenCheckedError` as well. You are setting `visible` to false in constructor and then to true in `ngOnInit()` in component, which AFAIK changes the value within the same event loop iteration. This throws the error on ngIf... – rynop Dec 03 '18 at 22:53
  • 4
    No doubt that this solution works. But ideally, service should hold API calls and http related stuff rather show/hide logic! – sabin Dec 31 '18 at 09:30
  • 2
    is this still working for angular 6? I seems like can't make it works, the nav.visible on nav component still showing false if onInit of other component I change it to true. – Nazrul Muhaimin Jan 14 '19 at 09:23
  • @NazrulMuhaimin I'm using Angular 8 and still working. – Arman Jan 04 '20 at 19:09
  • Will this actually work? As I understand, visible property is not a static member to the class NavbarService so every time you update the value it will be updated in that particular intanciated object and not across different object. Simplest solution is to make it static. – Rohan Shenoy Feb 11 '20 at 09:40
  • Nice service, but the events are needed in addition to solve things needed in runtime – Naga Sep 26 '22 at 15:13
21

I was able to solve this without using a nav/toolbar service by adding a data object to the route in the route.module. I expanded on Todd Motto's example of adding dynamic titles to a page and added toolbar: false/true to the data object in my path. I then subscribed to the router events in my toolbar.component. Using Todd's event listener func, I read the path object and used the boolean value to set the toolbar visible or not visible.

No service needed and works on pagerefresh.

routing module

...
const routes: Routes = [
{ path: 'welcome', component: WelcomeComponent, data: { title: 'welcome', toolbar: false} }, ...];

toolbar.component

constructor(private router: Router, private activatedRoute: ActivatedRoute, public incallSvc: IncallService) {
    this.visible = false; // set toolbar visible to false
  }

  ngOnInit() {
    this.router.events
      .pipe(
        filter(event => event instanceof NavigationEnd),
        map(() => this.activatedRoute),
        map(route => {
          while (route.firstChild) {
            route = route.firstChild;
          }
          return route;
        }),
      )
      .pipe(
        filter(route => route.outlet === 'primary'),
        mergeMap(route => route.data),
      )
      .subscribe(event => {
        this.viewedPage = event.title; // title of page
        this.showToolbar(event.toolbar); // show the toolbar?
      });
  }

  showToolbar(event) {
    if (event === false) {
      this.visible = false;
    } else if (event === true) {
      this.visible = true;
    } else {
      this.visible = this.visible;
    }
  }

toolbar.html

<mat-toolbar color="primary" *ngIf="visible">
  <mat-toolbar-row>
    <span>{{viewedPage | titlecase}}</span>
  </mat-toolbar-row>
</mat-toolbar>
cbilliau
  • 987
  • 9
  • 20
  • 2
    Thank you, this is the best approach that I saw here. :) – Ambrus Tóth Aug 25 '19 at 13:40
  • What are you doing with the pipe, filter, mergemap and subscribe? It looks like you're inspecting the route, and possibly setting the title based on the route? What does firstChild help with? (sorry I'm kinda a newb) – Dan Chase Nov 29 '19 at 00:51
  • 1
    Check out the link to Motto's example in my answer. It explains very well how he uses the rxjs operators (map,etc...) to get the data object in the route. You need to access the firstChild to get to the route data. Take your time with Motto's explanation and dive into each part as the example uses a lot of useful pieces of all the Activated Route parts in Angular. Good luck and keep asking questions! – cbilliau Nov 30 '19 at 14:44
  • 1
    Really very good answer, with great potential for usability in large-scale applications ! – Philip Developer Aug 11 '20 at 00:54
7

Adding to Dan's answer.

One more detail required for a complete answer. Which is registering the NavbarService as a provider for the whole application from app.module.ts

import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { FormsModule } from '@angular/forms';
import { HttpModule } from '@angular/http';

import { SharedModule } from './shared/shared.module';

import { AppComponent } from './app.component';
import { NavbarModule } from './navbar/navbar.module';
import { NavbarService } from './navbar/navbar.service';

import { AppRoutingModule, routedComponents } from './routing.module';

@NgModule({
    imports: [
        BrowserModule, FormsModule, HttpModule,
        NavbarModule,
        SharedModule,
        AppRoutingModule
    ],
    declarations: [
        routedComponents,
    ],
    providers: [
        // Here we register the NavbarService
        NavbarService  
    ],
    bootstrap: [AppComponent]
})
export class AppModule { }
Shanika Ediriweera
  • 1,975
  • 2
  • 24
  • 31
2

I like this answer by Dan above. However, it does create some update console errors, which I do not want in a production app. I would suggest instead using this method: answer.

It might also be helpful to use a canDeactivate to complete the implementation. Where I was hiding the navbar, such as on login, I added a navigate away 'canDeactive' service:

{ path: 'login', component: LoginComponent, canDeactivate: [NavigateAwayFromLoginDeactivatorService]  },

The deactivate service looks like this:

import { Injectable } from '@angular/core';
import { CanDeactivate } from '@angular/router';
import { LoginComponent } from "app/user/login/login.component";
import { NavbarTopService } from "app/navbar-top/navbar-top.service";

@Injectable()
export class NavigateAwayFromLoginDeactivatorService implements CanDeactivate<LoginComponent> {

  constructor(public nav: NavbarTopService) {  }

  canDeactivate(target: LoginComponent) {
    this.nav.show();
    return true;
  }
}

This way, I can hide only on login and do not need to call show() on every other component.

spinlok
  • 3,561
  • 18
  • 27
trees_are_great
  • 3,881
  • 3
  • 31
  • 62
  • 1
    Your approach is good. By using this approach, I implemented it for hiding the menu and header in the login component and header and menu show in the home component but the problem is when we refresh the browser window again header and menu are hidden from the home component. I am not getting what the exact problem is. Please help if possible. – Parvesh kumar Apr 27 '18 at 10:57
1

You can use ngIF directive on components where nav is located

   <nav *ngIf="this.currentRoute!=='login'" navigation>
   </nav>

after you get the current route:

  this.router.events.subscribe(event => {
  if (event.constructor.name === "NavigationEnd") {
    this.name = (<any>event).url.split("/").slice(-1)[0];
    this.isLogin = this.currentRoute === 'login';
  }
})
happyZZR1400
  • 2,387
  • 3
  • 25
  • 43
1

Add *ngIf='!showNav' in template

<nav class="navbar navbar-default navbar-fixed-top navClass" *ngIf='!showNav' >

And in LoginComponent

showNav = true;

This will show nav rest of the all the pages , if you want to hide in any pages just put showNav = true; in that component.

How it works :

First for it will check for showNav variable but it will not be available , so it will return false for the other pages where we want to show menu , so need to declare that variable any other pages.

In login page we set the value to true, so it will make it false and hide the nav.

Vivek Doshi
  • 56,649
  • 12
  • 110
  • 122
  • I have tested in my project and working great , did you get any error ? – Vivek Doshi Mar 30 '17 at 13:29
  • I don't get why you negate that boolean ... showNav = false would be a bit more comprehensible ... – downdrown Apr 16 '18 at 06:59
  • This works if you want to simply not have the nav HTML in the DOM, but unfortunately the components will still get initialized, which can be undesirable. – Judy007 Aug 30 '18 at 16:38
  • 1
    This does not work, if you have your navbar in an own component. Because NavbarComponent does not know the LoginComponent's implementation. – MAESTRO_DE Jun 19 '19 at 06:11
1

In order for it to work also add "Providers" wherever you're importing the NavbarService

navbar.component.ts and also example.component.ts

@Component({
  moduleId: module.id,
  selector: 'sd-navbar',
  templateUrl: 'navbar.component.html',
  providers: [NavbarService ]
})
Kenni
  • 11
  • 1
1

Another solution to this problem, specially if you are looking to open/close/toggle/ the side nav bar from other controls is to hold a reference to the side nav bar in a service as discussed below:

https://stackoverflow.com/a/48076331/1013544

this worked well for me as I had an application where the side nav was more like the root element and the router components were its content so they would be disabled in the background when the side nav menu is opened.

JaganY
  • 53
  • 4
0

The linked answer above by JaganY is the best answer if you are hiding a mat-sidenav element. You should never have simple code like this require change detection. Here is an example for other types of elements:

app.componenent.html

    <nav #rNav>
      <app-rightnav></app-rightnav>
    </nav>

app.componenent.ts

  @ViewChild('rNav') rNav!: ElementRef;

  constructor(public nav: NavbarService) { }

  ngAfterViewInit(): void {
    this.nav.setRight(this.rNav);
  }

navbar.service.ts

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

@Injectable({
  providedIn: 'root'
})
export class NavbarService {

  private right!: ElementRef;
  private visible!: boolean;

  hideR() { 
    this.visible = false;
    this.right.nativeElement.style.display = 'none';
  }

  showR() { 
    this.visible = true; 
    this.right.nativeElement.style.display = 'block';
  }

  toggleR() { this.visible ? this.hideR() : this.showR(); }

  setRight(e: ElementRef) {
    this.right = e;
  }
}

child-components.html

constructor() {
  this.nav.hideR(); // or this.nav.showR();
}
Jonathan
  • 3,893
  • 5
  • 46
  • 77