Angular 2 ng2-bootstrap modal inside child component called from parent component

Locohost picture Locohost · Feb 25, 2017 · Viewed 13.9k times · Source

Hard to explain this. I had a simple working ng2-bootstrap modal example working. I expanded it to include Boostrap 4 Jumbotron example template for my home page, now the ng2-bootstrap modal does nothing. I can see the this.childModal.show() in child modal component is getting called on the button click, but nothing happens. No error in Chrome console and no modal shows. I have no idea what to do next :-/

// ============================================================================
// /src/app/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 { ModalModule } from 'ng2-bootstrap/modal';
import { NgbModule } from '@ng-bootstrap/ng-bootstrap';

import { AppComponent } from './app.component';
import { DemoModalChildComponent } from './demo-modal-child.component';

@NgModule({
    declarations: [
        AppComponent, DemoModalChildComponent
    ],
    imports: [
        BrowserModule,
        FormsModule,
        HttpModule,
        NgbModule.forRoot(),
        ModalModule.forRoot()
    ],
    bootstrap: [AppComponent]
})
export class AppModule { }


// ============================================================================
// /src/app/app.component.ts

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

@Component({
    selector: 'app-root',
    templateUrl: './app.component.html',
    styleUrls: ['./app.component.css']
})
export class AppComponent {
    title = 'app works!';
}



<!-- ========================================================================== -->
<!-- /src/app/app.component.html -->

<nav class="navbar navbar-toggleable-md navbar-inverse fixed-top bg-inverse">
    <button class="navbar-toggler navbar-toggler-right" type="button" data-toggle="collapse" data-target="#navbarsExampleDefault"
        aria-controls="navbarsExampleDefault" aria-expanded="false" aria-label="Toggle navigation">
        <span class="navbar-toggler-icon"></span>
        </button>
        <a class="navbar-brand" href="#">Navbar</a>

        <div class="collapse navbar-collapse" id="navbarsExampleDefault">
            <ul class="navbar-nav mr-auto">
                <li class="nav-item active">
                    <a class="nav-link" href="/home">Home <span class="sr-only">(current)</span></a>
                </li>
                <li class="nav-item">
                    <a class="nav-link" (click)="showLoginModal()">Login</a>
                </li>
                <li class="nav-item">
                    <a class="nav-link" href="/members">Players</a>
                </li>
                <li class="nav-item">
                    <a class="nav-link" href="/game">Game</a>
                </li>
                <li class="nav-item">
                    <a class="nav-link disabled" href="#">Disabled</a>
                </li>
                <li class="nav-item dropdown">
                    <a class="nav-link dropdown-toggle" href="http://example.com" 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>
            <form class="form-inline my-2 my-lg-0">
                <input class="form-control mr-sm-2" type="text" placeholder="Search">
                <button class="btn btn-outline-success my-2 my-sm-0" type="submit">Search</button>
            </form>
        </div>
</nav>

<!-- Main jumbotron for a primary marketing message or call to action -->
<div class="jumbotron">
    <div class="container">
        <demo-modal-child #c="child"></demo-modal-child>
        <button type="button" class="btn btn-primary" (click)="c.showChildModal()">Open child modal</button>
        <h1 class="display-3">Hello, world!</h1>
        <p>This is a template for a simple marketing or informational website. It includes a large callout called a jumbotron
            and three supporting pieces of content. Use it as a starting point to create something more unique.</p>
        <p><a class="btn btn-primary btn-lg" href="#" role="button">Learn more &raquo;</a></p>
    </div>
</div>
<!-- /jumbotron -->

<div class="container">
    <!-- Example row of columns -->
    <div class="row">
        <div class="col-md-4">
            <h2>Heading</h2>
            <p>Donec id elit non mi porta gravida at eget metus. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum
                nibh, ut fermentum massa justo sit amet risus. Etiam porta sem malesuada magna mollis euismod. Donec sed
                odio dui.
            </p>
            <p><a class="btn btn-secondary" href="#" role="button">View details &raquo;</a></p>
        </div>
        <div class="col-md-4">
            <h2>Heading</h2>
            <p>Donec id elit non mi porta gravida at eget metus. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum
                nibh, ut fermentum massa justo sit amet risus. Etiam porta sem malesuada magna mollis euismod. Donec sed
                odio dui. </p>
            <p><a class="btn btn-secondary" href="#" role="button">View details &raquo;</a></p>
        </div>
        <div class="col-md-4">
            <h2>Heading</h2>
            <p>Donec sed odio dui. Cras justo odio, dapibus ac facilisis in, egestas eget quam. Vestibulum id ligula porta felis
                euismod semper. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa
                justo sit amet risus.</p>
            <p><a class="btn btn-secondary" href="#" role="button">View details &raquo;</a></p>
        </div>
    </div>

    <hr>

    <footer>
        <p>&copy; burnmarkGames 2017</p>
    </footer>
</div>
<!-- /container -->


// ============================================================================
// /src/app/demo-modal-child.component.ts

import { Component, ViewChild } from '@angular/core';
import { ModalDirective } from 'ng2-bootstrap/modal';

@Component({
    selector: 'demo-modal-child',
    templateUrl: './demo-modal-child.component.html',
    exportAs: 'child'
})
export class DemoModalChildComponent {
    @ViewChild('childModal') public childModal: ModalDirective;

    public showChildModal(): void {
        console.log('DemoModalChildComponent.showChildModal fired!');
        this.childModal.show();
    }

    public hideChildModal(): void {
        this.childModal.hide();
    }
}


<!-- ========================================================================== -->
<!-- /src/app/demo-modal-child.component.html -->

<div bsModal #childModal="bs-modal" class="modal fade" tabindex="-1" role="dialog"
    aria-labelledby="mySmallModalLabel" aria-hidden="true">
    <div class="modal-dialog modal-lg">
        <div class="modal-content">
            <div class="modal-header">
                <h4 class="modal-title pull-left">Please login...</h4>
                <button type="button" class="close pull-right" aria-label="Close" (click)="hideChildModal()">
          <span aria-hidden="true">&times;</span>
        </button>
            </div>
            <div class="modal-body">
                I am a child modal, opened from parent component!
            </div>
        </div>
    </div>
</div>

Answer

Aravind picture Aravind · Feb 26, 2017

Your common child modal component will be as below

import {Component,Input, ViewChild} from '@angular/core';
import { ModalDirective } from 'ng2-bootstrap/ng2-bootstrap';

@Component({
  selector: 'common-modal',
  template: `
   <div bsModal #childModal="bs-modal" class="modal fade" tabindex="-1" role="dialog" aria-labelledby="mySmallModalLabel" aria-hidden="true">
  <div class="modal-dialog modal-sm">
    <div class="modal-content">
      <div class="modal-header">
        <h4 class="modal-title pull-left">{{title}}</h4>
        <button type="button" class="close pull-right" aria-label="Close" (click)="hideChildModal()">
          <span aria-hidden="true">&times;</span>
        </button>
      </div>
      <div class="modal-body">
        <ng-content select=".modal-body"> </ng-content>
      </div>

      <div class="modal-footer">
        <div class="pull-left">
          <button class="btn btn-default" (click)="hide()"> Cancel </button>
        </div>
      </div>
    </div>
  </div>
</div>
  `,
})
export class CommonModalComponent {
   @ViewChild('childModal') public childModal:ModalDirective;
   @Input() title:string;
  constructor() {
  }
  show(){
    this.childModal.show();
  }
  hide(){
    this.childModal.hide();
  }
}

Using the child component in your parent component will look as below

import {Component, ViewChild, NgModule,ViewContainerRef} from '@angular/core'
import { BrowserModule } from '@angular/platform-browser';
import { ModalDirective,ModalModule } from 'ng2-bootstrap/ng2-bootstrap';
import {CommonModalComponent} from './child.modal';
@Component({
  selector: 'my-app',
  template: `
    <button type="button" class="btn btn-primary" (click)="childModal.show()">Open modal</button>
    <common-modal  #childModal [title]="'common modal'"> 
    <div class="modal-body">
    Hi heloo </div>
    </common-modal> 

  `,
})
export class AppComponent {
  @ViewChild('childModal') childModal :CommonModalComponent;
  constructor(private viewContainerRef: ViewContainerRef) {
  }

}

Using the above code you can have a separate common modal dialog which can be reused, so that your header & footer remains the same and you can use Content-Projection to use change the body of the modal dialog.

LIVE DEMO