Ankit Giri
Ankit Giri

Reputation: 105

How to open modal for multiple components from same parent component

I am using angular 9. I have a home page which has four buttons. I want to open separate modal on each button's click at a time. I did research a lot, here is my trial and effort. Thanks in advance :)

parent component

@Component({
  selector: 'app-link-budget-view',
  templateUrl: './home.component.html',
  styleUrls: ['./home.component.scss']
})
export class HomeComponent implements OnInit {
 
  @ViewChild('childOne') callChild_OneFn: ChildOneComponent;
  @ViewChild('childTwo') callChild_TwoFn: ChildTwoComponent;

  ...
  ngOnInit(): void {
     
  }
  openModalOne() {
    this.callChild_OneFn.OpenModalFunction();     
  }
  openModalOne() {
    this.callChild_TwoFn.OpenModalFunction();     

  ...
  }
}

Home Html

<button class="btn btn-primary mb-2 mr-2" (click)="openModalOne()">Modal 1</button>
<button class="btn btn-primary mb-2 mr-2" (click)="openModalTwo()">Modal 2</button>
 
...

 <app-child-one #childOne></app-child-one>
 
 <app-child-two #childTwo></app-child-two>

...

childOne Component

@Component({
      selector: 'app-link-budget-view',
      templateUrl: './child-one.component.html',
      styleUrls: ['./chile-one.component.scss']
    })

    export class ChildOneComponent implements OnInit {
      
     constructor(  private modalService: NgbModal) { }
    
     ngOnInit(): void {  
     } 
 
     OpenModalFunction() {
       console.log("component One function running...")
       this.modalService.open('#ModalOneId', { size: 'xl' });
     }    

      ...
      
    }

similarly there is a function in component two

OpenModalFunction() {
   console.log("component Two function running...")
   this.modalService.open('#ModalTwoId', { size: 'xl' });
 }  

Component One Html

<ng-template #ModalOneId let-modal>
    <div class="modal-header">
        <h4 class="modal-title">This is modal one</h4>
        <button type="button" class="close" aria-label="Close" (click)="modal.dismiss('Cross click')">
            <span aria-hidden="true">&times;</span>
        </button>
    </div>
    <div class="modal-body">
        <div class="card">
            <div class=" card-body">
                <div id="table" class="table-editable">
                  ...
                  ...
                </div>
            </div>
        </div>
    </div>
    <div class="modal-footer">
        <button type="button" class="btn btn-light" (click)="modal.close('Close click')">Close</button>
    </div>
</ng-template>

similarly ng-templete is used in component two html

Upvotes: 2

Views: 6142

Answers (3)

Ankit Giri
Ankit Giri

Reputation: 105

Finally I fixed it here is the code to help others

Parent ts

export class ParentComponent implements OnInit {

  constructor(private modalService: CustomModalServiceService) { }

  ngOnInit(): void {
  }

  openModal(modalName: string) {
    this.modalService.openModalFunction(modalName);
  }
}

Parent html

 <button class="btn btn-primary mb-2 mr-2" (click)="openModal('childOne')">Modal_1</button>
    <button class="btn btn-primary mb-2 mr-2" (click)="openModal('childTwo')">Modal_2</button>

child one or two ts code accordingly for one or two

export class ChildOneComponent implements OnInit {
 
  constructor(private modalRef: NgbActiveModal) { }

  ngOnInit(): void {
  }

  hideModalFunction() {
    this.modalRef.close();
   }
}

child one or two html for modal

<div class="modal-header">
    <h4 class="modal-title">This is modal one</h4>
    <button type="button" class="close" aria-label="Close" (click)="hideModalFunction()">
        <span aria-hidden="true">&times;</span>
    </button>
</div>
<div class="modal-body">
    <div class="card">
        <div class=" card-body">
            <div id="table" class="table-editable">
              ...
              ...
            </div>
        </div>
    </div>
</div>
<div class="modal-footer">
    <button type="button" class="btn btn-light" (click)="hideModalFunction()">Close</button>
</div>

and you need a custom modal service

Custom modal service

export class CustomModalService {
 ...
 private modalRef: NgbModalRef;
 constructor(  private modalService: NgbModal) { }

 ngOnInit(): void {  
 } 
 
 openModalFunction(modalName: string) {
       switch(modalName) {
         case 'one':
           console.log("component One function running...");
           // do any execution before opening child component
           this.modalRef = this.modalService.open(ChildOneComponent, { size: 'xl' });
           this.modalRef.componentInstance.testData = 'test';
           break;
         case 'two':
           console.log("component Two function running...");
           // do any execution before opening child component
          this.modalRef = this.modalService.open(ChildTwoComponent, { size: 'xl' });
          break;
         case default:
           // do nothing
       }
  }
  
}

Upvotes: 1

Rick
Rick

Reputation: 1870

you didn't mention if what you wrote is working or not, or what errors you are getting. it looks like it should work to me. Although if you just want to open a modal, you can handle it completely in your html:

<button (click)="childOne.OpenModalFunction()">Modal 1</button>
<button (click)="childTwo.OpenModalFunction()">Modal 2</button>

<app-child-one #childOne></app-child-one>
<app-child-two #childTwo></app-child-two>

that should be the only thing you need in the parent. no code needed in the parent .ts file. if this is not working, there's something wrong with the code in your child class.

Upvotes: 1

Syed Sharique
Syed Sharique

Reputation: 56

You are repeating too much code here. You could make it more re-usable and less repetitive. Try to extract most of the business logic to a service. Leave the component as dumb(no business logic) as possible for better re-usability.

Parent component

@Component({
  selector: 'app-link-budget-view',
  templateUrl: './home.component.html',
  styleUrls: ['./home.component.scss']
})
export class HomeComponent implements OnInit {
  ...
  constructor(private myModalService: MyModalService)
  ngOnInit(): void {
     
  }
  openModal(modelName: string) {
    this.myModalService.openModalFunction(modalName);     
  }
}

Home Html

<button class="btn btn-primary mb-2 mr-2" (click)="openModal('one')">Modal 1</button>
<button class="btn btn-primary mb-2 mr-2" (click)="openModal('two')">Modal 2</button>
 
...

MyModal Service

@Injectable({
  providedIn: 'root'
})

export class MyModalService {
 ...
 private modalRef: NgbModalRef;
 constructor(  private modalService: NgbModal) { }

 ngOnInit(): void {  
 } 
 
 openModalFunction(modalName: string) {
       switch(modalName) {
         case 'one':
           console.log("component One function running...");
           // do any execution before opening child component
           this.modalRef = this.modalService.open(ChildOneComponent, { size: 'xl' });
           this.modalRef.componentInstance.testData = 'test';
           break;
         case 'two':
           console.log("component Two function running...");
           // do any execution before opening child component
          this.modalRef = this.modalService.open(ChildTwoComponent, { size: 'xl' });
         case default:
           // do nothing
       }
  }
  hideModalFunction() {
    //do something before closing the modal
    this.modalRef.close();
  }
}

Now no OpenModalFunction() required inside the child components as you have extracted the modal opening logic into the MyModalService. This will also adhere to the Single Functionality principle means every component has only single functionality to be fulfilled. Furthermore, you have now full control via MyModalService to handle popups. If you want to execute a common code before/after opening/closing the windows like notifying another service, you could do that.

Now this could be further optimized as all software codes do. Like extracting common code from Child Components to a single interface/abstract class. But it totally depends upon your requirements.

Upvotes: 0

Related Questions