Search code examples
angularbootstrap-modalbootstrap-5bootstrap5-modal

bootstrap5 Cannot convert undefined or null to object


I am trying to create a new modal using angular 11 and bootstrap 5 but getting the belwo error

ERROR TypeError: Cannot convert undefined or null to object
    at Function.keys (<anonymous>)
    at Object.getDataAttributes (bootstrap.esm.js:875)
    at Modal._getConfig (bootstrap.esm.js:2821)
    at new Modal (bootstrap.esm.js:2681)
    at BaCarouselComponent.ngAfterViewInit (ba-carousel.component.ts:26)
    at callHook (core.js:2573)
    at callHooks (core.js:2542)
    at executeInitAndCheckHooks (core.js:2493)
    at refreshView (core.js:9537)
    at refreshComponent (core.js:10637)

Component code:

import {AfterViewInit, Component, ElementRef, OnInit, ViewChild} from '@angular/core';
import {Modal} from 'bootstrap';

@Component({
  selector: 'app-home',
  templateUrl: './home.component.html',
  styleUrls: ['./home.component.scss']
})
export class HomeComponent implements OnInit, AfterViewInit {

  modalDirect: Modal | undefined;
  @ViewChild('demoModal') demoModal: ElementRef<HTMLElement> | any;
  constructor() { }

  ngOnInit(): void {
    // this.modalDirect = new Modal(this.demoModal, {});
  }

  ngAfterViewInit(): void {
    this.modalDirect = new Modal(this.demoModal, {});
    console.log('ngAfterViewInit: %o', this.demoModal);
  }

  open(element: Element): void {
    this.modalDirect = new Modal(element, {});
    // this.modalDirect.Modal.open(element);
    // this.modal.show();
  }

}

Html template code:


<div
  #demoModal class="modal fade"
  data-bs-backdrop="static"
  data-bs-keyboard="false"
  tabindex="-1"
  aria-labelledby="staticBackdropLabel"
  aria-hidden="true">

  <div class="modal-dialog">
    <div class="modal-content">
      <div class="modal-header">
        <h5 class="modal-title" id="staticBackdropLabel">Modal title</h5>
        <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
      </div>

      <div class="modal-body">
        ...
      </div>

      <div class="modal-footer">
        <button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Close</button>
        <button type="button" class="btn btn-primary">Understood</button>
      </div>

    </div>

  </div>

</div>

Solution

  • Demo using nativeElement StackBlitz Demo Link

    When You are accessing dom element using @ViewChild() its angular way of getting ElementRef from DOM, and Angular ElementRef is a wrapper around a native element inside of a View. So Simply to access real DOM we need nativeElement and this instance returns us current real DOM object. Thats why we need nativeElement when we use @ViewChild() to access real DOM.

    ngAfterViewInit(): void {
       this.modalDirect = new Modal(this.demoModal.nativeElement, {});
    }
    

    Now, Second scenario is, You dont need to use @ViewChild(), just pass template Reference variable to .open() method, and creates new Modal() instance from open() method. In this case, You are directly passing real DOM to open() method thats why You dont need nativeElement.

    open(element: any): void {
      this.modalDirect = new Modal(element, {});
      this.modalDirect.show();
    } 
    

    and call this open method from template like this..

    <div #demoModal class="modal fade" data-bs-backdrop="static" data-bs-keyboard="false" tabindex="-1"
    

    aria-labelledby="staticBackdropLabel" aria-hidden="true"> -------------

     <button class="btn btn-secondary" (click)="open(demoModal)">open</button>
    

    In Above, demoModal is template-reference variable of div tag, and we are passing this element reference directly to open method, so no need to use nativeElement here int this case.

    Demo without nativeElement Stackblitz Link