Search code examples
angulartypescriptngx-bootstrap

ngx-bootstrap + Angular 12 Open modal from another component


I'm trying to create a common ngx-bootstrap modal component in Angular.

I've searched the internet and tried this way.

import { Component } from '@angular/core';
import { BsModalService } from 'ngx-bootstrap/modal';
import { BsModalRef } from 'ngx-bootstrap/modal/modal-options.class';

/* This is the Component from which we open the Modal Component */
@Component({
  selector: 'my-component',
  templateUrl: './service-component.html'
})
export class MyComponent {
  bsModalRef: BsModalRef;
  constructor(private modalService: BsModalService) {}

  public openModalWithComponent() {
    /* this is how we open a Modal Component from another component */
    this.bsModalRef = this.modalService.show(ModalContentComponent);
  }
}

/* This is the Modal Component */
@Component({
  selector: 'child-modal',
  template: `
    <div class="modal-header">
      <h4 class="modal-title pull-left">Title</h4>
      <button type="button" class="close pull-right" aria-label="Close" (click)="bsModalRef.hide()">
        <span aria-hidden="true">&times;</span>
      </button>
    </div>
    <div class="modal-body">
      ...
    </div>
    <div class="modal-footer">
      <button type="button" class="btn btn-default" (click)="bsModalRef.hide()">Close</button>
    </div>
  `
})
export class ChildModalComponent {
  constructor(public bsModalRef: BsModalRef) {}
}

<button type="button" class="btn btn-primary" (click)="openModalWithComponent()">Create modal with component</button>

<child-modal #childModal ></child-modal>

But it says 'Cannot find name 'ModalContentComponent'.

And I really don't understand where they get that from, although it seems to work for other people.


Solution

  • Finally I followed this other tutorial which worked for me.

    The following example is used to add items to an array:

    Create a simple component ItemList to display a list of items added to an Array.

    ng g c itemList
    

    item-list.component.html

    <div>
        <h1> Item List </h1>
        <ul>
        <li *ngFor=”let item of itemList”>{{item}}</li>
        </ul>
        <button type=”button” class=”btn btn-primary”>Add New Item</button>
    </div>
    

    item-list.component.ts

    export class ItemListComponent implements OnInit {
      itemList = ["Book","Pen"]
      constructor() { }
      ngOnInit() {
      }
    }
    

    Let’s create another component to use as the modal.

    ng g c itemAdd
    

    The second component to add a new item, which will use as a modal in the future. Also, I have written a simple function called saveToList() which will also implement in the future. For now, I have put a simple console log, just to display the value in the input field.

    numberOfItems will be the count of items in the existing array.

    item-add.component.html

    <div class="modal-body">
        <h4>Add Item</h4>
        <form [formGroup]="itemform" (ngSubmit)="saveToList(itemform)">
            <div class="col-md-6">
                <label>Number of Items : </label> {{numberOfItems}}
            </div>
            <div class="col-md-6">
                <label>Item Name</label>
            </div>
            <div class="col-md-6">
                <input type="text" class="form-control" formControlName="name">
                <button class="button mt-1" type="submit">Save</button>
            </div>
        </form>
    <div>
    

    item-add.component.ts

    export class ItemAddComponent implements OnInit {
         itemform;
         numberOfItems = 0;
         constructor(private formBuilder: FormBuilder) {
             this.itemform = this.formBuilder.group({
             name: ""
             })
         }
    
         ngOnInit() {
         }
    
         saveToList(form) {
            console.log(form.value);
         }
    }
    

    Add a modal.
    Let's change the code to open add item form as a modal when clicking the Add New Item button.

    item-list.component.ts

    export class ItemListComponent implements OnInit {
     itemList = ["Book","Pen"];
     bsModalRef: BsModalRef;
     constructor(private modalService: BsModalService) { }
     
     ngOnInit() {
     }
     
     openModalWithComponent() {
       this.bsModalRef = this.modalService.show(ItemAddComponent);
       this.bsModalRef.content.closeBtnName = 'Close'; 
     }
    }
    

    Don't forget to add ItemAddComponent into entryComponents list in app.module.ts

    providers: [],
    entryComponents: [ItemAddComponent],
    

    Pass values from parent to modal.
    We can simply use initialState to pass an initial value into the modal .

    item-list.component.ts

    openModalWithComponent() {
        const initialState = {
            list: [
                "test"
            ]
        };
        this.bsModalRef = this.modalService.show(ItemAddComponent, {initialState});
        this.bsModalRef.content.closeBtnName = 'Close'; 
    }
    

    To read this value from add Item modal.
    Initialize a list inside modal, item-add.component.ts

    list: any[] = [];
    

    Then the initialState list can access as “list[]”.
    We are sending a sample data “test” and it can be accessed as list[0] from item-add.component.html

    <label>Number of Items : </label> {{list[0]}}
    

    Think that we want to pass an object from the item list into modal.

    item-list.component.ts

    openModalWithComponent() {
      const initialState = {
        list: [
          {"tag":'Count',"value":this.itemList.length}
        ]
      };
      this.bsModalRef = this.modalService.show(ItemAddComponent, {initialState});
      this.bsModalRef.content.closeBtnName = 'Close'; 
    }
    

    --

    <label>Number of Items : </label> {{list[0].value}}
    

    Pass values from modal to child.
    Let's see how to pass a value to the item list from the add item form. Declare an EventEmitter inside the item-add.component.ts

    public event: EventEmitter<any> = new EventEmitter();
    

    Emit an event when saving the item.

    triggerEvent(item: string) {
        this.event.emit({ data: item , res:200 });
    }
    

    item-add.component.ts

    export class ItemAddComponent implements OnInit {
      itemform;
      numberOfItems = 0;
      list: any[] = [];
      public event: EventEmitter<any> = new EventEmitter();
      constructor(private formBuilder: FormBuilder, public bsModalRef: BsModalRef) {}
    
      ngOnInit() {
        this.itemform = this.formBuilder.group({
          name: ""
        })
      }
    
      saveToList(form) {
        this.triggerEvent(form.value.name);
        this.bsModalRef.hide();
      }
    
      triggerEvent(item: string) {
        this.event.emit({ data: item , res:200 });
      }
    }
    

    And inside the item-list.component.ts we can subscribe to the event emitter.

    item-list.component.ts

    openModalWithComponent() {
      const initialState = {
        list: [
          {"tag":'Count',"value":this.itemList.length}
        ]
      };
      this.bsModalRef = this.modalService.show(ItemAddComponent, {initialState});
      this.bsModalRef.content.closeBtnName = 'Close';
    
      this.bsModalRef.content.event.subscribe(res => {
        this.itemList.push(res.data);
      });
    }