5

I have a ngx bootstrap modal that currently works and opens a Details Listing page with specific data for everyone of my Listings. However I would like to use routing so that my Listing Modal can be opened via direct url and populated with listing specific details based on it's id. For example .../listings/listing/50 would open the Listing Modal and be populated with the details for listing id 50.

I can't even get any modal to open via direct url at this time.

Currently I am opening each modal with a link click

<a (click)="openDetailsModal(listing)" ...

and my ListingService

  openDetailsModal(listing: Listing): void {
    this.selectedListing = listing;
    this.bsModalRef = this.modalService.show(ListingDetailComponent, {class:'listingDetailModal'});
    this.bsModalRef.content.listing = this.selectedListing;
  }

Also I am currently getting all of my listings from my database as well using HttpClient

JDinar
  • 115
  • 1
  • 3
  • 9

1 Answers1

7

You can use the standard bootstrap modal in this scenario. Add the modal markup to your component and then load it via routing. I've added the "show" class to the modal so that it will display right away. Make sure you have a router-outlet on your parent component. Here is a demo on Stackblitz.

Your listing modal component would look like this:

import { Component, Input, Output, EventEmitter, AfterViewInit } from '@angular/core';
import { Router } from '@angular/router';

@Component({
    selector: 'listing-dialog',
    template: `
    <div id="backdrop" class="modal-backdrop fade" [ngClass]="{ show: showModal }"></div>
    <div class="modal d-block fade" 
      [ngClass]="{ show: showModal }"
      (click)="onClose()"
      id="listing-dialog" 
      tabindex="-1" role="dialog" aria-labelledby="modalIdLabel">
        <div class="modal-dialog" role="document" (click)="onDialogClick($event)">
            <div class="modal-content">
                <div class="modal-header">
                    <h5>I was loaded via route</h5>
                    <button type="button"
                        class="close"
                        (click)="onClose()"
                        aria-label="Close"><span aria-hidden="true">&times;</span>
                    </button>
                </div>
                <div class="modal-body">
                  <p>Add some detail here.</p>
                </div>
                <div class="modal-footer">
                  <button type="button" class="btn btn-primary" (click)="onClose()">Close</button>
                </div>
            </div>
        </div>
    </div>
    `
})
export class ListingDialogComponent implements AfterViewInit {

    showModal = false;

    constructor(private router: Router) { }

    ngAfterViewInit() {
      this.showModal = true;
    }

    onClose() {
      this.showModal = false;
      //Allow fade out animation to play before navigating back
      setTimeout(
        () => this.router.navigate(['..']),
        100
      );
    }

    onDialogClick(event: UIEvent) {
      // Capture click on dialog and prevent it from bubbling to the modal background.
      event.stopPropagation();
      event.cancelBubble = true;
    }
}

Your hosting component would have something like this:

<a [routerLink]="['listing']" >Load Listing</a>
<router-outlet></router-outlet>

The module would need to register the routes:

import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { FormsModule } from '@angular/forms';
import { Routes, RouterModule } from '@angular/router';

import { AppComponent } from './app.component';
import { HelloComponent } from './hello.component';
import { ListingDialogComponent } from './listing-dialog.component';

const routes: Routes = [
  { path: 'listing', component: ListingDialogComponent }
]

@NgModule({
  imports:      [ BrowserModule, FormsModule, RouterModule.forRoot(routes) ],
  declarations: [ AppComponent, HelloComponent, ListingDialogComponent ],
  bootstrap:    [ AppComponent ]
})
export class AppModule { }

I did not include parameters in the demo, but you can easily observe them in the modal and then perform any necessary service calls to populate the modal. I use this method in many places in my apps.

Joel Richman
  • 1,894
  • 12
  • 13
  • Thank you @Joel, I can get it to work this way. I tried making the modal fade in and out but I can't seem to get it to work, do you know how I can accomplish this? – JDinar Sep 01 '18 at 12:47
  • Also is there a way to close the modal on (click) on the backdrop? Sorry for the novice questions, really new to angular – JDinar Sep 01 '18 at 12:51
  • @JDinar I updated the answer and the StackBlitz to show how to get the fade animation to work and to close when clicking on the backdrop. – Joel Richman Sep 02 '18 at 05:27
  • Amazing job again, thank you it works perfectly. Before you answered I tried doing it on my own and ended up creating an entire directive for being able to click the backdrop to close it. Your solution is much more elegant. – JDinar Sep 10 '18 at 17:11
  • 1
    Thank you for your answer but a note for anyone that might be seeing this with the latest angular version this.showModal = true; should be on ngOnInit since if placed on afterviewinit it will cause angular to throw a property after checked changed exception. Works great otherwise! – Harry Sep 17 '19 at 13:42