Changing the Angular 5 ng-bootstrap Modal Dialog template dynamically in real-time

I'm currently developing a modal dialog that requires the ability to dynamically change the templateURL. The code shown is the default template, but I need to be able to swap it out dynamically. I'm unsure of how to achieve this, as the templateURL name and location will be passed in. Below is an excerpt of my component code:

import { ModalService } from './../../services/modal.service';
import {Component, Input} from '@angular/core';

import {NgbModal, NgbActiveModal} from '@ng-bootstrap/ng-bootstrap';

@Component({
  selector: 'app-ngbd-modal-content',
  templateUrl: '../../templates/modal.tp1.html'
})

export class ModalOptionsComponent {
  @Input() name;

  constructor(public activeModal: NgbActiveModal, modelService: ModalService) {}
}

export class  NgbdModalComponent {
  constructor(private ngbModal: NgbModal) {}
}

Ideally, I would like to trigger the modal from my service class rather than the component, but I'm unsure how to proceed with that. Despite researching extensively, I haven't found much information on how to accomplish this.

Modal.service.ts:

import { ModalOptionsComponent } from './../pages/modal-options/modal-options.component';
import { Injectable } from '@angular/core';
import {NgbModal, NgbActiveModal} from '@ng-bootstrap/ng-bootstrap';

@Injectable()
export class ModalService {

  constructor(private ngbModal: NgbModal, private modalComp: ModalOptionsComponent) { }

  modalService(modal: any) {
    const modalDefaults = {
      templateUrl: '../templates/modal.tp1.html'
    };
    const modalOptions = {
      hasClose: true,
      closeButtonText: 'Close',
      actionButtonText: 'OK',
      headerText: 'Proceed?',
      bodyText: 'Perform this action?',
      okResult: {}
    };
  }

  showModal(customModalDefaults: any, customModalOptions: any) {

  }


}

Another requirement is to create a service class for this functionality. I am new to Angular and seeking guidance on how to achieve this.

Answer №1

Due to the limitations in changing the templateUrl, I had to resort to creating a component for each specific dialog type required. Although this approach sacrificed some dynamism, it adequately serves my current needs. Essentially, the service can be utilized to produce the necessary component type. Unfortunately, implementing dynamic component loading was not a feasible option due to its complexity. Below is an illustration of my approach.

Code snippet for default modal dialog component:

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

import {NgbModal, NgbActiveModal} from '@ng-bootstrap/ng-bootstrap';

@Component({
  selector: 'app-ngbd-modal-content',
  template: `
<div class="modal-header">
<h3 class="font-32" style="padding-bottom: 0px">{{headerText}}</h3>
</div>
<div class="modal-body">
<p>{{bodyText}}</p>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-outline-dark" (click)="activeModal.close('Close click')">Close</button>
</div>
  `,
  styleUrls: ['./default-modal-dialog.component.scss']
})
export class DefaultModalDialogComponent {
  @Input() bodyText;
  @Input() headerText;

  constructor(public activeModal: NgbActiveModal) {}

}

Code snippet for FeaturedSearch component:

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

    import {NgbModal, NgbActiveModal} from '@ng-bootstrap/ng-bootstrap';

    @Component({
    selector: 'app-featured-search-dialog',
    template: `
    <div>
        <div class="modal-header">
            <h3 class="font-32" style="margin-bottom: -16px;">Edit Heading and Subheading</h3>
        </div>
        <div class="modal-body" style="padding:30px;">
            <div class="row">
                <div class="col-md-12">
                    <label class="font-14-bold">Heading</label>
                </div>
            </div>
            <div class="row">
                <div class="col-md-12">
                    <input style="box-shadow: rgb(102, 102, 102) 0px 0px 4px inset; border: 0px; margin-bottom: 50px;"  type="text" class="form-control input-sm" ng-model="modalOptions.okResult.heading" >
                </div>
            </div>
            <div class="row">
                <div class="col-md-12">
                    <label class="font-14-bold">Subheading</label>
                </div>
            </div>
            <div class="row">
                <div class="col-md-12">
                    <input style="box-shadow: rgb(102, 102, 102) 0px 0px 4px inset; border: 0px;" type="text" class="form-control input-sm" ng-model="modalOptions.okResult.subheading" >
                </div>
            </div>
        </div>
        <div class="modal-footer">
            <div style="margin-top: 8px; margin-bottom: 8px;">
                <button style="background-color: #999999; color: #ffffff;" type="button" ng-show="modalOptions.okResult.buttonText !== 'Close'"
                        class="btn font-14-bold"
                        data-ng-click="modalOptions.close()">Close
                </button>
                <button style="background-color: #004065; color: #ffffff; width: 70px;" type="button" class="btn ng-binding" ng-disabled="modalOptions.okResult.heading.length <= 0 || modalOptions.okResult.subheading.length <=0" data-ng-click="modalOptions.ok()">OK</button>
            </div>
        </div>
    </div>
    `,
    styleUrls: ['./featured-search-dialog.component.scss']
    })
    export class FeaturedSearchDialogComponent {

    @Input() heading;
    @Input() subheading;

    constructor(public activeModal: NgbActiveModal) {}

    }

Custom Modal Service :

import { Injectable, Input, ComponentFactoryResolver } from '@angular/core';
import {NgbModal, NgbActiveModal} from '@ng-bootstrap/ng-bootstrap';
@Injectable()
export class ModalService {
    constructor(private ngbModal: NgbModal,
                private componentFactoryResolver: ComponentFactoryResolver) {}

  showDefaultModalComponent(theComponent: any, headerText: any, bodyText: any) {
    const componenetFactory = this.componentFactoryResolver.resolveComponentFactory(theComponent);
    const modalRef = this.ngbModal.open(theComponent);
    modalRef.componentInstance.bodyText = bodyText;
    modalRef.componentInstance.headerText = headerText;
    return modalRef;
  }

  showFeaturedDialog(theComponent: any, heading: any, subheading: any) {
    const componenetFactory = this.componentFactoryResolver.resolveComponentFactory(theComponent);
    const modalRef = this.ngbModal.open(theComponent);
    return modalRef;
  }


}

Answer №2

Initially, NgbModal and NgbActiveModal are derived from the package '@ng-bootstrap/ng-bootstrap'.

Creating a separate service for the 'dynamic template' feature may not be necessary, as the package itself provides the necessary functionality. Here's how you can achieve it:

Look at the scenario depicted in the image below (assuming that's the case). The 'default-modal' in your main component, which in my instance is located in the -detail(folder), triggers the modal (default-modal) from that component (-detail.component).

Suppose you are editing an event in the *-detail.component by clicking a button. This action prompts the 'default-modal' to pop up. Let's take a look at the code.

*-.detail.component.html

<button class="btn" (click)="editEvents(eventvalues)"> Edit </button>

*-detail.component.ts

editEvents(events) {
const activeModal = this.modalService.open(DefaultModal, { size: 'lg' });
activeModal.componentInstance.modalHeader = 'Edit Event';
activeModal.componentInstance.eventdata = events;  }

Now, onto the Modal Component: modal.component.html

<p>{{modalHeader}}</p>
<p> {{eventdata }} </p>

Ensure to make the necessary imports in your files. When importing in the component module, include NgbModalModule in the imports array. In the component.ts file: import NgbModal and { DefaultModal } from './default-modal/default-modal.component';

https://i.sstatic.net/kYmQH.png

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Module 'ngx-bootstrap' not found in project

My application is encountering an issue with ngx-bootstrap where the module can no longer be detected unless the path is specified. For instance: import { BsModalService, BsModalRef } from 'ngx-bootstrap'; results in "Cannot find module ' ...

How can one gracefully extract the complete URL from the ActivatedRouteSnapshot?

When working with Angular route guards, there is a need to set up the "next" path for redirection after a successful login. The standard guard function signature canActivate looks like this: public canActivate(route: ActivatedRouteSnapshot): Observable&l ...

Is it possible to eliminate the array from a property using TypeScript?

Presenting my current model: export interface SizeAndColors { size: string; color: string; }[]; In addition to the above, I also have another model where I require the SizeAndColors interface but without an array. export interface Cart { options: ...

Understanding how types intersect in TypeScript

I'm currently diving into Type Relations in TypeScript. Can someone help explain what happens when we intersect the types represented by these two expressions: {a:number}[] & {b:string}[] Does this result in {a:number, b:string}[] ? Any clarificat ...

@Viewchild doesn't have access to matSort

In my Angular project, I am having trouble getting my @ViewChild instance to work with MatSort in HTML. component.ts file: import { MatSort } from '@angular/material'; export class MyComponent { @ViewChild(MatSort) sort: MatSort; } ngOn ...

Can the Date class be expanded by overloading the constructor method?

In my dataset, there are dates in different formats that Typescript doesn't recognize. To address this issue, I developed a "safeDateParse" function to handle extended conversions and modified the Date.parse() method accordingly. /** Custom overload ...

One way to update the value of the current array or object using ngModel in Angular 2 is to directly

I have a situation where I am dealing with both an array and an object. The array is populated with data retrieved from a service, while the object contains the first element of that array. feesEntries: Array<any> = []; selectedFeesEntry: any; clien ...

Apply a dynamic function to assign a background color to a specific class

Currently, I have a function called getBackground(items) that returns a background color from a JSON file list. Within my application, there is a component that automatically adds a class name (.item-radio-checked) when a user clicks on an item in the list ...

Encountering RxJS errors during the process of constructing an object using streams retrieved from Firebase

I am currently in the process of developing a template-driven form that involves multiple streams from Firebase. Despite my efforts, I keep encountering errors with the json pipe. The error message I receive is "Converting circular structure to JSON as ...

The state update is triggering a soft refresh of the page within Next.js

In my Next.js project, I have integrated a modal component using Radix UI that includes two-way bound inputs with state management. The issue arises when any state is updated, triggering a page-wide re-render and refreshing all states. Here is a snippet of ...

The Angular route functions flawlessly in the development environment, but encounters issues when deployed to

I have a project built with Angular 2, During development on localhost, everything runs smoothly. However, once I build a production version using (npm run build: prod) and navigate to the route on the server, I encounter a 404 error indicating that the r ...

What is the best way to pass a value to a modal and access it within the modal's component in Angular 8?

How can I trigger the quickViewModal to open and send an ID to be read in the modal component? Seeking assistance from anyone who can help. Below is the HTML code where the modal is being called: <div class="icon swipe-to-top" data-toggle="modal" da ...

When receiving JSON and attempting to store the data in a variable, I encounter an issue where it returns the error message "undefined is not iterable (cannot read property Symbol

I'm currently integrating the Advice Slip API into my project. I am experiencing an issue when trying to store the JSON data in a variable like so: let advice; fetch("https://api.adviceslip.com/advice").then(response => response.json()). ...

Divs are not being organized into rows correctly due to issues with Bootstrap styling

I have implemented Bootstrap in my Angular application. The stylesheet link is included in my Index.html file as follows: <link rel="stylesheet" href="../node_modules/bootstrap/dist/css/bootstrap.css"> In addition to that, I have listed Bootstrap a ...

issues arise post-transpilation with creating errors

In order to practice, I decided to create a basic TypeScript project. If it could be helpful, here is my ts.config: { "compilerOptions": { "target": "es2016", "module": "commonjs", "outDir": "./dist", "esModuleInterop": true, "forceC ...

The object returned by bodyParser is not a string but a data structure

I have been working on implementing a JSON content listener in Typescript using Express and Body-parser. Everything is functioning perfectly, except when I receive the JSON webhook, it is logged as an object instead of a string. import express from 'e ...

Tips for correctly decorating constructors in TypeScript

When a class is wrapped with a decorator, the superclasses lose access to that classes' properties. But why does this happen? I've got some code that demonstrates the issue: First, a decorator is created which replaces the constructor of a cla ...

Performance issues with the Django API causing significant delays in data retrieval

Currently, I am working on integrating Angular2 with Django (a Python framework). I have successfully created a basic API in Django that displays all the records from the database. You can access the API via the following link: [https://djangoshopnroar.he ...

Transform Observable RxJS into practical results

In a straightforward scenario, I have an upload action on the page that looks like this: onUpload$: Subject<SomeUpload> = new Subject<SomeUpload>(); uploadAction$: Observable<Action> = this.onUpload$.map(entity => this.someActionServi ...

Please indicate the data type in Vue using Typescript

I'm currently struggling with Vue (3) + Typescript while attempting to specify a data property with a specific type. I have created a .d.ts file, but unfortunately, it hasn't helped. This is what I'm trying: import Modeler from 'bpmn-js ...