How to create a custom modal in angular
Tags: angular, tutorial, typescript, javascript
Published: 2021-07-13 17:32:48

##I) Introduction
Modal is simply an interactive window that hides the principal page to provide the user options according to his action. 
 
For example, given a list of items with a delete button for each row, when the user clicks the delete button, a modal appears requiring the user to either confirm his choice (delete item) or close the modal. As you may notice, modal interaction is a great choice since it offers a nice user experience.

In this quick tutorial, we will try to build a custom reusable modal with angular, which makes it easy to maintain and reuse.

##II) Modal elements

_To build this modal we need 3 main elements_

* Modal service which is responsible for creating/destroying modal.

* Modal component containing modal information ( _body_, _title_, _buttons_ ) and, it sends events to modalService ( _confirm_/_close_ ).
 
* Hosting component contains a reference to where the modal is going to appear and listen to modalService events. It does not know anything about the modal.   

![Angula-modal](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/3hr3asewjgqbuvn18u8o.jpg) 
  <figcaption> Modal service, Modal component, Home component interaction overview </figcaption>


_NB: At the end of each section, you find the code associated with._

********************************  

 <h3>1)Modal service</h3> 

**************************************************** 
 
_Our service will hold 3 methods:_ 
 
***openModal*** : which is responsible for creating modal and add it to a specific component (hosting component).
 
***closeModal*** : which is responsible for destroying modal, after the user clicks on the close button.
 
***confirm*** : which is the event triggered when the user clicks on confirm button, it emits "confirm" to hosting component.


_Our service will hold 2 private attributes:_

  
***componentRef*** : which keeps track of the modal created.
 
***componentSubscriber*** : which is a subject returned after the modal created. it is responsible for sending events to the component holding the modal. 

_let's dig deep down in our methods:_  

######openModal

_openModal method will receive 3 parameters:_ 

_entry_: which is the modal container (where the modal is going to appear) of type __viewContainerRef__.  
_Angular doc:_
> __viewContainerRef__ _represents a container where one or more views can be attached to a component_.

 
_modalTitle_: which is the modal title.
 
_modalBody_: representing modal body. 
 
The question here is how to create a modal component, and add it to the modal container ? :flushed:

_entry_ ( __viewContainerRef__ ) has a magic method called, createComponent. This method instantiate a component and add it to the __viewContainerRef__ (_entry_). It takes a component factory and returns component instance, which gives us access to component instance and related objects. 

But how to create a component factory from a component? :confused:

Angular provides us a __componentFactoryResolver__ class that takes a component and returns a __componentFactory__. 
 
_Angular doc:_
> __componentFactoryResolver__ _is a simple registry that maps Components to generated ComponentFactory classes that can be used to create instances of components_. 

>__ComponentFactory__ _is the base class for a factory that can create a component dynamically. Instantiate a factory for a given type of component with resolveComponentFactory()._

Great !! :smiley:
 
We injected this class ( _ComponentFactoryResolver_ ) in service constructor, and we created a componentFactory ( __factory__ ) in openModal method.

After modal instance created, we are able then to provide the modal by ***title*** _input_, ***body*** _input_, and subscribe to different output events ( ***closeMeEvent*** , ***confirmEvent*** ) to handle them in user interaction.
  
Then, in this method we return a subject as observable, this observable is useful to the hosting component to be notified of user interaction with the modal ( "confirm" ) to do specific logic.

######closeModal :
 
This method is simple, it is responsible for destroying the modal and complete the subscriber. Hosting component is not concerned by this event.

######confirm :

which is responsible for sending to the hosting component "confirm" and then close the modal   

```typescript 
export class ModalService {
  private componentRef!: ComponentRef<ModalComponent>;
  private componentSubscriber!: Subject<string>;
  constructor(private resolver: ComponentFactoryResolver) {}

  openModal(entry: ViewContainerRef, modalTitle: string, modalBody: string) {
    let factory = this.resolver.resolveComponentFactory(ModalComponent);
    this.componentRef = entry.createComponent(factory);
    this.componentRef.instance.title = modalTitle;
    this.componentRef.instance.body = modalBody;
    this.componentRef.instance.closeMeEvent.subscribe(() => this.closeModal());
    this.componentRef.instance.confirmEvent.subscribe(() => this.confirm());
    this.componentSubscriber = new Subject<string>();
    return this.componentSubscriber.asObservable();
  }

  closeModal() {
    this.componentSubscriber.complete();
    this.componentRef.destroy();
  }

  confirm() {
    this.componentSubscriber.next('confirm');
    this.closeModal();
  }
}
```  
 <figcaption> ModalService.ts </figcaption>

*********************************************** 
  
 <h3>2)Modal component</h3> 

*************************************
Our modal component is a simple angular component containing : 
***title*** : as @input() 
***body*** : as @input() 
***closeMeEvent***: as @Output() 
***confirmEvent***: as @Output() 
***closeMe*** : emitting closeMeEvent to modalService 
***confirm*** : emitting confirmEvent to modalService  
```typescript
export class ModalComponent implements OnInit, OnDestroy {
  constructor() {}
 
  @Input() title: string = '';
  @Input() body: string = '';
  @Output() closeMeEvent = new EventEmitter();
  @Output() confirmEvent = new EventEmitter();
  ngOnInit(): void {
    console.log('Modal init');
  }

  closeMe() {
    this.closeMeEvent.emit();
  }
  confirm() {
    this.confirmEvent.emit();
  } 

 ngOnDestroy(): void {
    console.log(' Modal destroyed');
  }

}

 
```
 <figcaption> ModalComponent.ts </figcaption>



************************************* 
 
  <h3>3)Hosting component </h3>

********************************************** 
Hosting component, is the component where the modal is going to be created. Home component in our example.

This component must have a HTML reference element representing the modal container, and the event triggering modal creation ( ***createModal*** ).
 
The host component is responsible for providing modal service by __viewContainerRef__ (entry). To get this view we use __@viewChild__ decorator with the reference element specified in the view(_#modal_).
  
_Angular doc:_
> __@viewChild__ _is a property decorator that configures a view query. The change detector looks for the first element or the directive matching the selector in the view DOM_

This component is responsible also for subscribing to modalService openModal to listen for events streams. If it receives "confirm", our logic will be executed.

That's it ! your modal is working fine. :muscle:

```typescript 
export class HomeComponent implements OnInit, OnDestroy {
  constructor(private modalService: ModalService) {}

  @ViewChild('modal', { read: ViewContainerRef })
  entry!: ViewContainerRef;
  sub!: Subscription;

  ngOnInit(): void {}
  createModal() {
    this.sub = this.modalService
      .openModal(this.entry, 'Are you sure ?', 'click confirm or close')
      .subscribe((v) => {
        //your logic
      });
  }
  ngOnDestroy(): void {
    if (this.sub) this.sub.unsubscribe();
  }
}

 
```   

 <figcaption> HomeComponent.ts </figcaption>

 

```HTML 
<button (click)="createModal()">Delete me</button>
<div #modal></div> 
```

 <figcaption> HomeComponent.html </figcaption>



##III) Conclusion

In this tutorial, we proposed a general pattern of how to implement a modal in angular, which consists of three elements:modal component, modal service, and hosting component. 
 
Hope it is clear and helpful. Comment below for any suggestions, clarification or issues. 

Waiting for your feedback concerning this pattern :v:.  

You find full code in my github repo [Angular reusable modal pattern](https://github.com/hssanbzlm/angular_reusable_modal_pattern)