Jason Swett
Jason Swett

Reputation: 45094

Force collapse of Bootstrap 4 navbar on Angular 2 route change

I want to implement something like the following the "right" way in Angular 2:

$('.collapse').hide();

What would be the "Angular 2 way" of doing this? Do I just use native JavaScript? Are there built-in Angular methods I should use?

Edit: Let me add some context for my particular case.

I have a Bootstrap 4 navbar with a collapsible nav. If you pull down the nav, then click a link, the nav doesn't disappear like you would expect it to.

I want it so that when you click any link anywhere, the navbar goes back to its collapsed state.

Here's what my navbar markup looks like:

<nav class="navbar navbar-toggleable-md navbar-inverse bg-inverse fixed-top">

  <button class="navbar-toggler navbar-toggler-right"
          type="button"
          data-toggle="collapse"
          data-target="#foodie-navbar"
          aria-controls="foodie-navbar"
          aria-expanded="false"
          aria-label="Toggle navigation">

    <span class="navbar-toggler-icon"></span>
  </button>

  <a class="navbar-brand" routerLink="/">Foodie</a>

  <div class="collapse navbar-collapse" id="foodie-navbar">
    <ul class="navbar-nav mr-auto">
      <li class="nav-item" *ngIf="auth.authenticated()">
        <a class="nav-link" [routerLink]="['/places']">Places</a>
      </li>
      <li class="nav-item">
        <a class="nav-link" routerLink="" (click)="auth.login()" *ngIf="!auth.authenticated()">Log In</a>
        <a class="nav-link" routerLink="" (click)="auth.logout()" *ngIf="auth.authenticated()">Log Out</a>
      </li>
    </ul>
  </div>
</nav>

<div class="container">
  <router-outlet></router-outlet>
</div>

Here's what my AppComponent looks like:

import { Component } from '@angular/core';
import { Router, NavigationEnd } from '@angular/router';
import { Auth } from './auth.service';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css'],
  providers: [Auth]
})
export class AppComponent {
  constructor(private auth: Auth, private router: Router) {
    router.events.subscribe(val => {
      if (val instanceof NavigationEnd) {
        // This is when the hiding should happen.
      }
    })
  }
}

By the way, now that I look a little closer, I see that if I do $('.collapse').hide(); on the console, then click the hamburger menu again, it doesn't work. So maybe I need a different solution altogether.

Edit: Someone marked this question a duplicate of an ng2-bootstrap question. My question/answer don't have anything to do with ng2-bootstrap so I don't believe it's a duplicate.

Upvotes: 14

Views: 6614

Answers (4)

TCB13
TCB13

Reputation: 3155

Bootstrap has built in javascript to open and close the menu when the user clicks on the hamburger menu. This handled by data-bs-toggle, data-bs-target and can also be applied to the menu links:

<a class="nav-link" 
  [routerLink]="['/']" href="#" 
  data-bs-toggle="collapse" 
  data-bs-target="#navbarNavDropdown"
>Home</a>

Tested in Bootstrap 5 + Angular 15. Bootstrap installed with npm install --save [email protected] @types/bootstrap. No further configuration is required in Angular.

Upvotes: 0

Junior
Junior

Reputation: 43

I have another Solution

Had a bit of inspiration from this post, but also some research by myself. One of the articles I read: https://medium.com/@tiboprea/build-a-responsive-bootstrap-4-navbar-in-angular-5-without-jquery-c59ad35b007

So, in this solution, it is possible to collapse by clicking on the toggle button but also when the route changes

//nav.component.html

<nav class="navbar navbar-expand-lg navbar-dark bg-dark">
 <a class="navbar-brand" href="#">
  Brand
 </a>

<button class="navbar-toggler" type="button" (click)="toggleNavbar()">
   <span class="navbar-toggler-icon"></span>
</button>

<div class="collapse navbar-collapse" [ngClass]="{ 'show': navbarOpen }">
  <ul class="navbar-nav mr-auto">
   <li class="nav-item">
    <a class="nav-link" href="#">Item 1</a>
   </li>

   <li class="nav-item">
    <a class="nav-link" href="#">Item 2</a>
   </li>
  </ul>
 </div>
</nav>

//nav.component.ts

import { Router, NavigationEnd } from '@angular/router';
import { filter } from 'rxjs/operators';

export class NavComponent implements OnInit {

constructor(private router: Router) {}


ngOnInit(): void {

  this.router.events
    .pipe(filter(evt => evt instanceof NavigationEnd))  
    .subscribe((evt: NavigationEnd) => {
      this.navbarOpen = false;
    });
  }

  toggleNavbar() {
    this.navbarOpen = !this.navbarOpen;
  }
 }

Upvotes: 1

user2502767
user2502767

Reputation: 328

Here's what I did which worked.

  1. Open app.component.ts

  2. Import Router and NavigationEnd:

    import { Router, NavigationEnd } from '@angular/router';

  3. Just under the imports insert the following in order to access JQuery:

    declare var $: any;

  4. Add the following into the constructor:

    private router: Router

  5. In the ngOnInit() add the following:

    this.router.events.subscribe((evt) => { if (!(evt instanceof NavigationEnd)) { return; } // Scroll to the top of the page window.scrollTo(0, 0); // This closes the top nav $(document).click((event) => { const click = $(event.target); const _open = $('.navbar-collapse').hasClass('show'); if (_open === true && !click.hasClass('navbar-toggler')) { $('.navbar-toggler').click(); } }); });

I'm using NG 10 with bootstrap 5 but I know it works in previous version of both NG and Bootstrap 4.5.x

Upvotes: 2

Jason Swett
Jason Swett

Reputation: 45094

I was able to achieve what I wanted by triggering a click on the navbar toggle button.

In both pieces of code below, probably the main thing to pay attention to is the collapseNav() function.

Here's my component code:

import { Component, ViewChild, ElementRef } from '@angular/core';
import { Auth } from './auth.service';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css'],
  providers: [Auth]
})
export class AppComponent {
  @ViewChild('navbarToggler') navbarToggler:ElementRef;

  constructor(private auth: Auth) {}

  navBarTogglerIsVisible() {
    return this.navbarToggler.nativeElement.offsetParent !== null;
  }

  collapseNav() {
    if (this.navBarTogglerIsVisible()) {
      this.navbarToggler.nativeElement.click();
    }
  }
}

And here's my markup.

<nav class="navbar navbar-toggleable-md navbar-inverse bg-inverse fixed-top">

  <button #navbarToggler class="navbar-toggler navbar-toggler-right"
          type="button"
          data-toggle="collapse"
          data-target="#foodie-navbar"
          aria-controls="foodie-navbar"
          aria-expanded="false"
          aria-label="Toggle navigation">

    <span class="navbar-toggler-icon"></span>
  </button>

  <a class="navbar-brand" routerLink="/">Foodie</a>

  <div class="collapse navbar-collapse" id="foodie-navbar">
    <ul class="navbar-nav mr-auto">
      <li class="nav-item" *ngIf="auth.authenticated()">
        <a (click)="collapseNav()" class="nav-link" [routerLink]="['/places']">Places</a>
      </li>
      <li class="nav-item">
        <a (click)="collapseNav(); auth.login()" class="nav-link" routerLink="" *ngIf="!auth.authenticated()">Log In</a>
        <a (click)="collapseNav(); auth.logout()" class="nav-link" routerLink="" *ngIf="auth.authenticated()">Log Out</a>
      </li>
    </ul>
  </div>
</nav>

<div class="container">
  <router-outlet></router-outlet>
</div>

Upvotes: 28

Related Questions