ivanao
ivanao

Reputation: 235

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.

Upvotes: 0

Views: 5278

Answers (2)

ivanao
ivanao

Reputation: 235

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);
  });
}

Upvotes: 1

danday74
danday74

Reputation: 57231

Basically, you create a new component called ModalContentComponent - just use the CLI to do this:

ng generate component /path/modal-content

then import the newly created ModalContentComponent as usual wherever you reference it in your .ts code (e.g. the code you've given in your question) and voila!

Obviously make sure you declare ModalContentComponent in your module in the usual manner (if you have things setup correctly the CLI should handle this for you)

OPTIONAL ADDITIONAL INFO:

To make modals more reusable you probably want the HTML in modal content component to use content projection, which would involve using an additional component that supports content projection.

Upvotes: 1

Related Questions