13

I have followed the following to install bootstrap 4 into my Angular 2 project: Accepted Answer, following the first 1,2,3 and 4 steps

However when I add the following HTML to my header component:

<nav class="navbar-dark bg-inverse">
<div class="container">
    <a href="#" class="navbar-brand"></a>
    <ul class="nav navbar-nav float-xs-right">
        <li class="nav-item dropdown">
            <a href="#" class="nav-link dropdown-toggle" id="nav-dropdown" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">SomeEmail@hotmail.com</a>
            <div class="dropdown-menu" aria-labelledby="nav-dropdown">
                <a href="#" class="dropdown-item">Sign Out</a>
            </div>
        </li>
    </ul>
</div>

As you can see its a dropdown basically, when I click on the dropdown the page refreshes, instead it doesn't display the 'sign out' option.

This is my angular-cli.json styles section:

"styles": [
  "styles.css",
  "../node_modules/bootstrap/dist/css/bootstrap.min.css"
],    

And inside my Angular 2 module:

import { NgbModule } from '@ng-bootstrap/ng-bootstrap';

I then import NgbModule into the imports section.

I've clearly missed something, can someone shed any light into what it maybe exactly?

Community
  • 1
  • 1
Code Ratchet
  • 5,758
  • 18
  • 77
  • 141
  • 1
    Here's how to add ng-bootstrap to your module: https://ng-bootstrap.github.io/#/getting-started. And here's how ng-bootstrap dropdowns work: https://ng-bootstrap.github.io/#/components/dropdown. You can't just use the documentation of bootstrap and hope the exact same markup will work with ng-bootstrap: the point of ng-bootstrap is to allow using bootstrap components like dropdown in an angular way. – JB Nizet Dec 24 '16 at 23:18
  • Thanks for the links! helped me out massively! got it working using ngbDropdown – Code Ratchet Dec 24 '16 at 23:41
  • Notice that bootstrap 4 is alpha and is not good idea to use it for projects. Because it will have lots of changes and is not stable. Just use it for learning for future. – Artin Falahi Dec 25 '16 at 08:18
  • @HassanFalahi I'm only using it for learning. – Code Ratchet Dec 26 '16 at 02:30

14 Answers14

16
  1. Please install ng-bootstrap from this link Getting Started with the following command:

    npm `install --save @ng-bootstrap/ng-bootstrap`
    
  2. Import it on app.module.ts like

    import `{NgbModule} from '@ng-bootstrap/ng-bootstrap';` 
    
  3. Import on

    imports:[
       NgbModule.forRoot(),
    ]
    
  4. Add ngbDropdown on dropdown

  5. Add ngbDropdownToggle on dropdown Toggle DOM

  6. Add ngbDropdownMenu on dropdown menu DOM

                <li ngbDropdown  class="nav-item dropdown" >
                    <a ngbDropdownToggle class="nav-link dropdown-toggle"  href="#" id="navbarDropdownMenuLink" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
                     Manage
                    </a>
                    <div ngbDropdownMenu  class="dropdown-menu" aria-labelledby="navbarDropdownMenuLink">
                      <a class="dropdown-item" href="#">Save Data</a>
                      <a class="dropdown-item" href="#">Fetch Data</a>
    
                    </div>
                  </li>
              </ul>
    
Martin Evans
  • 45,791
  • 17
  • 81
  • 97
15

Variation on @VictorLuchian for newest BS4 beta, it seems 'show' class need to be added on the dropdown-menu as well. This version include a click outside close instead of mouseout

import { Directive,HostListener,HostBinding, ElementRef } from '@angular/core'; 
@Directive({
  selector: '[customdropdown]'
})
export class CustomDropdownDirective {

private isOpen: boolean =false;
constructor(private _el: ElementRef) { 

}

@HostBinding('class.show') get opened() {
    return this.isOpen;
}
@HostListener('click') open() {
    this.isOpen = true;
    this._el.nativeElement.querySelector('.dropdown-menu').classList.add('show')                
}
@HostListener('document:click', ['$event.target']) close (targetElement) {
    let inside: boolean = this._el.nativeElement.contains(targetElement);
    if(!inside) {
        this.isOpen = false;
        this._el.nativeElement.querySelector('.dropdown-menu').classList.remove('show')
    }
}
}
Adrien Pavillet
  • 373
  • 4
  • 9
9

Some plugins and CSS components depend on other plugins. If you include plugins individually, make sure to check for these dependencies in the docs. Also note that all plugins depend on jQuery (this means jQuery must be included before the plugin files).

In .angular-cli.json add the following lines to the scripts section:

# version 4.x
  "scripts": [
    "../node_modules/jquery/dist/jquery.js",
    "../node_modules/bootstrap/dist/js/bootstrap.js",
  ]

Checkout definition here

Hidayt Rahman
  • 2,490
  • 26
  • 32
  • It depends on tether lib as well. Add it to the angular.cli.json "../node_modules/jquery/dist/jquery.js", **"../node_modules/tether/dist/js/tether.min.js",** "../node_modules/bootstrap/dist/js/bootstrap.js", – Sriram Jan 26 '18 at 04:58
  • Correct.. but not necessary – Hidayt Rahman Oct 23 '18 at 06:46
9

Like Andrien's said, you can simplify the code like this.

constructor(private _el: ElementRef) { }

@HostBinding('class.show') isOpen = false;

@HostListener('click') toogleOpen() {
    this.isOpen = !this.isOpen;
    this._el.nativeElement.querySelector('.dropdown-menu').classList.toggle('show')
}
dpolicastro
  • 1,379
  • 1
  • 14
  • 22
3

You will need to add a dropdown directive in order for this to work. The directive looks like this:

import { Directive,HostListener,HostBinding } from '@angular/core'; 
@Directive({
selector: '[appcustomdropdown]'
})
export class CustomdropdownDirective {

constructor() { }

@HostBinding('class.open') get opened()
{
return this.isOpen;
}
@HostListener('click') open()
{
this.isOpen=true;
}
@HostListener('mouseleave') close()
{
this.isOpen=false;
}
private isOpen=false;
}

Then, you will add the attribute like so:

<li class="nav-item dropdown" appcustomdropdown >
Victor Luchian
  • 112
  • 1
  • 10
  • 1
    With Bootstrap 4.0.0-alpha6 Dropdown with buttons, I changed 'class.open' to 'class.show' to get it to work. – slasky Jul 13 '17 at 17:46
1

Example Code StackBliz Link

Firstly Bootstrap 3 & 4 Drop down active state classes are different

  1. In Boostrap 3 : In dropdown open state; .dropdown-toggle parent element is added 'open' CSS class

  2. Where as in Boostrap 4 make use of "show" CSS class In drop down open state. Here .dropdown-toggle element's parent element and next sibling element with CSS class .dropdown-menu are added "show" css Class

Hence to make boostrap 4 dropdown working with angular 4 we will create new angular directive class and add it to boostrap dropdown in angular template

Step 1 - Crate new angular directive in ng-boostrap-dropdown.directive.ts

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

@Directive({
  selector: '[appNgBoostrapDropdown]'
})
export class NgBoostrapDropdownDirective {

  private isShow: boolean = false;
  private dropdownParentEl = this.elementRef.nativeElement.closest('.dropdown');
  constructor(private elementRef: ElementRef) { }


  @HostListener('click') open() {
    this.isShow = !this.isShow;
    if (this.isShow) {
      this.dropdownParentEl.classList.add('show');
      this.dropdownParentEl.querySelector(".dropdown-menu").classList.add('show');
    } else {
      this.dropdownParentEl.classList.remove('show');
      this.dropdownParentEl.querySelector(".dropdown-menu").classList.remove('show');
    }
  }

  @HostListener('document:click', ['$event'])
  clickout(event) {
    if (this.elementRef.nativeElement.contains(event.target) && this.isShow) {
      this.dropdownParentEl.classList.add('show');
      this.dropdownParentEl.querySelector(".dropdown-menu").classList.add('show');
    } else {
      this.dropdownParentEl.classList.remove('show');
      this.dropdownParentEl.querySelector(".dropdown-menu").classList.remove('show');
      this.isShow = false;
    }
  }

}

Step 2 : Import this Directive in app.module.ts

import { NgBoostrapDropdownDirective } from './directives/ng-boostrap-dropdown.directive';

@NgModule({

  declarations: [ AppComponent, NgBoostrapDropdownDirective ],

})

Step 3 : Apply directive in Template using appNgBoostrapDropdown

NAVIGATION : 
      <li class="nav-item dropdown" >
            <a class="nav-link dropdown-toggle" appNgBoostrapDropdown  href="#" id="dropdown01" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">Dropdown</a>
            <div class="dropdown-menu" aria-labelledby="dropdown01">
              <a class="dropdown-item" href="#">Action</a>
              <a class="dropdown-item" href="#">Another action</a>
              <a class="dropdown-item" href="#">Something else here</a>
            </div>
          </li>
        </ul>

BUTTON DROPDOWN : 

<div class="dropdown">
  <button class="btn btn-secondary dropdown-toggle" appNgBoostrapDropdown type="button" id="dropdownMenuButton" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
    Dropdown button
  </button>
  <div class="dropdown-menu" aria-labelledby="dropdownMenuButton">
    <a class="dropdown-item" href="#">Action</a>
    <a class="dropdown-item" href="#">Another action</a>
    <a class="dropdown-item" href="#">Something else here</a>
  </div>
</div>

Example Code StackBliz Link

Rahul Talar
  • 314
  • 6
  • 15
  • When I click on the drop down it redirects to the other page. And drop down is not opening anyway in this solution as well. I assume it's because a tag that redirects. –  Nov 19 '18 at 16:27
1

Inspired by Rahul Talar's first version(Step 1 - Crate new angular directive in ng-boostrap-dropdown.directive.ts), i've made some similar using Rendere2

import { Directive, HostListener, ElementRef, Renderer2, OnInit } from '@angular/core';

@Directive({
  selector: '[appDropdown]'
})
export class AppDropdownDirective implements OnInit {
  private isShow = false;
  private classShow = 'show';
  private parentNode: HTMLElement;
  private siblingNode: HTMLElement;

  constructor(private elementRef: ElementRef, private renderer: Renderer2) {}

  ngOnInit() {
    this.parentNode = this.renderer.parentNode(this.elementRef.nativeElement);
    this.siblingNode = this.renderer.nextSibling(this.elementRef.nativeElement);
  }

  @HostListener('click') open() {
    this.isShow = !this.isShow;
    if (this.isShow) {
      this.addClass();
    } else {
      this.removeClass();
    }
  }

  @HostListener('document:click', ['$event']) clickout(event) {
    if (this.elementRef.nativeElement !== event.target && this.isShow) {
      this.removeClass();
      this.isShow = false;
    }
  }

  private addClass() {
    this.renderer.addClass(this.parentNode, this.classShow);
    this.renderer.addClass(this.siblingNode, this.classShow);
  }

  private removeClass() {
    this.renderer.removeClass(this.parentNode, this.classShow);
    this.renderer.removeClass(this.siblingNode, this.classShow);
  }
}
oleg gabureac
  • 803
  • 7
  • 10
1

With Angular 8 and Bootstrap 4.2 this is the working solution I'm using :

1- First I've created a custom directive. What it does is listen to the on click event of the dropdown container and toggle the .show class of the .dropdown-menu element (Bootstrap 4 standard behavior). Also, it will close the dropdown menu if there's a click everywhere else on the document.

import {Directive, ElementRef, HostBinding, HostListener, OnInit} from '@angular/core';

@Directive({
  selector: '[appDropdown]'
})
export class DropdownDirective implements OnInit {
  dropDownMenu: HTMLElement;
  @HostListener('document:click', ['$event']) toggleOpen(event: Event) {
    if ( this.dropDownButton.nativeElement.contains(event.target) ) {
      this.dropDownMenu.classList.toggle('show');
    } else {
      this.dropDownMenu.classList.remove('show');
    }
  }

  constructor(private dropDownButton: ElementRef) { }

  ngOnInit(): void {
    this.dropDownMenu = this.dropDownButton.nativeElement.querySelector('.dropdown-menu');
  }
}

2- Once your directive is created and registered. Be sure to apply the directive to the dropdown element, like on this example:

<div class="btn-group" appDropdown>
    <button type="button" class="btn btn-primary dropdown-toggle">Dropdown menu<span class="caret"></span></button>
    <ul class="dropdown-menu">
        <li class="dropdown-item"><a href="#">Item One</a></li>
        <li class="dropdown-item"><a href="#">Item Two</a></li>
        <li class="dropdown-item"><a href="#">Item Three</a></li>
    </ul>
</div>
Dayron Gallardo
  • 1,502
  • 2
  • 21
  • 37
0

The CSS needs to be in the <head></head> tag.
I quote from bootstrap

Copy-paste the stylesheet <link> into your <head> before all other stylesheets to load our CSS.

I continue quoting

Add our JavaScript plugins, jQuery, and Tether near the end of your pages, right before the closing tag. Be sure to place jQuery and Tether first, as our code depends on them.

Make sure that you have in this way here is the link to bootstrap documentation where i get this quotes https://v4-alpha.getbootstrap.com/getting-started/introduction/

and check this answer in the post that you share the link https://stackoverflow.com/a/39809447/3284537

Community
  • 1
  • 1
Emiliano
  • 698
  • 9
  • 30
0

Somebody has created a new version of the template library specifically design for Angular 2+. According to the website, it was developed by ng-team, although the link it gives gets a 404 response. It does work, though, and I have used it in multiple places throughout my current project. You'll just have to pull in the library with npm. All of the instructions are on this website:

http://valor-software.com/ngx-bootstrap/#/

That page shows you all of the install and usage instructions you need to get started. I hope this helps!

Grungondola
  • 680
  • 6
  • 26
  • The page you link to is not a project by bootstrap – Stefan Z Camilleri Aug 02 '17 at 10:43
  • It works very will with Angular 4, though, and is a great resource. It uses all of the bootstrap objects and provides working templates for all of them. According to the website, it was made by ng-team, which I missed before. – Grungondola Aug 03 '17 at 12:10
  • Yes, I'm aware of it, using it myself on a project in fact. My comment was merely to correct the fact that it is not supplied by bootstrap :) Thank you for correcting your answer. – Stefan Z Camilleri Aug 03 '17 at 17:01
0

i have used a different approach for getting drop down on collpased navbar.

STEP 1 Add click event on navbar toggle button

<button type="button" class="navbar-toggle" data-toggle="collapse" (click)="OnClik()" data-target="#myNavbar"

Html

    <nav class="navbar navbar-inverse">
  <div class="container-fluid">
    <div class="navbar-header">
      <button type="button" class="navbar-toggle" data-toggle="collapse" (click)="OnClik()" data-target="#myNavbar">
        <span class="icon-bar"></span>
        <span class="icon-bar"></span>
        <span class="icon-bar"></span>
      </button>
      <a class="navbar-brand" href="#">WebSiteName</a>
    </div>
    <div class="collapse navbar-collapse " [ngClass]="{'show': buttontoggled}" id="myNavbar">
      <ul class="nav navbar-nav">
        <li class="active"><a href="#">Home</a></li>
        <li class="dropdown">
          <a class="dropdown-toggle" data-toggle="dropdown" href="#">Page 1 <span class="caret"></span></a>
          <ul class="dropdown-menu">
            <li><a href="#">Page 1-1</a></li>
            <li><a href="#">Page 1-2</a></li>
            <li><a href="#">Page 1-3</a></li>
          </ul>
        </li>
        <li><a href="#">Page 2</a></li>
        <li><a href="#">Page 3</a></li>
      </ul>
      <ul class="nav navbar-nav navbar-right">
        <li><a href="#"><span class="glyphicon glyphicon-user"></span> Sign Up</a></li>
        <li><a href="#"><span class="glyphicon glyphicon-log-in"></span> Login</a></li>
      </ul>
    </div>
  </div>
</nav>

Step2: implement the function inside Navbar component.ts (Above navbar html template is used inside navbar component)

import { Component} from '@angular/core';
    export class HeaderComponent  {
      buttontoggled:boolean:false;

    OnClik(){
      this.buttontoggled=!this.buttontoggled;
    }

Step 3 based on the navbar toggle button click add class show(bootstrap 4) or open for previous bootstrap versions. we can use ngClass Directive for this

 <div class="collapse navbar-collapse " [ngClass]="{'show': buttontoggled}" id="myNavbar">

Work flow

Navbar toggle button will be visible when navbar geting collapsed for smaller resolutions

by handling the button click event we can set one flag to check if the button clicked or not

Based on this flag we will we will bind the css class show to navabr div using ngClass directive

Lijo
  • 6,498
  • 5
  • 49
  • 60
0

I am using theme which is not build for Angular and it contain classic bootstrap, I had same issue and fixed by modifying bootstrap.js file.

Issue is that bootstrap listen events with $(document).on and problem is in "document" part.

In my bootstrap file it is line 18001

 /**
   * ------------------------------------------------------------------------
   * Data Api implementation
   * ------------------------------------------------------------------------
   */


  $(document).on(Event.KEYDOWN_DATA_API, Selector.DATA_TOGGLE, Dropdown._dataApiKeydownHandler).on(Event.KEYDOWN_DATA_API, Selector.MENU, Dropdown._dataApiKeydownHandler).on(Event.CLICK_DATA_API + " " + Event.KEYUP_DATA_API, Dropdown._clearMenus).on(Event.CLICK_DATA_API, Selector.DATA_TOGGLE, function (event) {
    event.preventDefault();
    event.stopPropagation();

    Dropdown._jQueryInterface.call($(this), 'toggle');
  }).on(Event.CLICK_DATA_API, Selector.FORM_CHILD, function (e) {
    e.stopPropagation();
  });

Change $(document) to $("body") and it will work.

 $("body").on(Event.KEYDOWN_DATA_API, Selector.DATA_TOGGLE, Dropdown._dataApiKeydownHandler).on(Event.KEYDOWN_DATA_API, Selector.MENU, Dropdown._dataApiKeydownHandler).on(Event.CLICK_DATA_API + " " + Event.KEYUP_DATA_API, Dropdown._clearMenus).on(Event.CLICK_DATA_API, Selector.DATA_TOGGLE, function (event) {
        event.preventDefault();
        event.stopPropagation();

        Dropdown._jQueryInterface.call($(this), 'toggle');
      }).on(Event.CLICK_DATA_API, Selector.FORM_CHILD, function (e) {
        e.stopPropagation();
      });
Dimash
  • 581
  • 1
  • 5
  • 13
0

navbar.component.html

<li class="nav-item dropdown show" appDropdown>
        <a class="nav-link dropdown-toggle" id="navbarDropdown" role="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
          Categories
        </a>
        <div class="dropdown-menu" aria-labelledby="navbarDropdown">
          <a routerLink="yourLInk1" class="dropdown-item">Item 1</a>
          <a routerLink="yourLInk2" class="dropdown-item">Item 2</a>
          <a routerLink="yourLInk3" class="dropdown-item">Item 3</a>
          <a routerLink="yourLInk4" class="dropdown-item">Item 4</a>
          <div class="dropdown-divider"></div>
          <a routerLink="yourLInk5" class="dropdown-item">Item 5</a>
        </div>
      </li>

dropdown.directive.ts

import { Directive, HostBinding, HostListener, ElementRef } from '@angular/core';

    @Directive({
      selector: '[appDropdown]'
    })
    export class DropdownDirective {
      constructor(private elementRef: ElementRef) { }

      private dropdownParentEl = this.elementRef.nativeElement.closest('.dropdown');

      @HostListener('mouseenter') toggleOpen(){
        this.dropdownParentEl.querySelector(".dropdown-menu").classList.add('show');
      }
      @HostListener('mouseleave') toggleClose(){
        this.dropdownParentEl.querySelector(".dropdown-menu").classList.remove('show');
      }

      @HostListener('click') toogleOpen() {
          this.dropdownParentEl.querySelector(".dropdown-menu").classList.toggle('show');
      }
    }
0
import { Directive, HostListener, HostBinding, ElementRef } from '@angular/core';

@Directive({
  selector: '[appDropdown]'
})
export class DropdownDirective {
  constructor(private _el: ElementRef) { 
  }
  @HostBinding('class.show') isOpen = false;
  @HostListener('click') toggleOpen(){
    this.isOpen=!this.isOpen;
    if(this.isOpen){
      this._el.nativeElement.querySelector('.dropdown-menu').classList.add('show'); 
    }
    else{
      this._el.nativeElement.querySelector('.dropdown-menu').classList.remove('show');
    }
  }

}