Search code examples
angulartypescriptinheritancengx-modal

Ngx-Modal when extended 'NativeElement of undefined' Angular 2


This plunker should open up a modal when the button is pressed. I have extended the existing ngx-modal but it throws error: Cannot read property 'nativeElement' of undefined.

Having looked at the console this is because "modalRoot" should be programatically assigned as a ViewChild handler for the modal. It doesn't seem to get defined when extended even though I've added the super() to my constructor, any ideas?

Plunker that shows issue

    //our root app component
import {Component, NgModule, HostListener, ElementRef} from '@angular/core'
import {BrowserModule} from '@angular/platform-browser'
import {ModalModule} from "ngx-modal";
import { Modal } from "ngx-modal";

@Component({
    selector: 'ext-ngx-modal', 
    template: `<ng-content></ng-content>`,
})
export class NgxModalComponent extends Modal {
    constructor() {
        super();
    }

    openExt():void {
      this.open();
    }

    @HostListener('document:keydown', ['$event'])
    onkeydown(ev: KeyboardEvent) {
        console.log("this.isOpened: " + this.isOpened;
    }
}

@Component({
  selector: 'my-app',
  template: `
    <div>
      <h2>Hello {{name}} I am </h2>
      <div class="row container-fluid">
        <button (click)="myExtNgxModal.openExt()"> open my modal</button>
        <ext-ngx-modal #myExtNgxModal>
          <modal>
              <modal-header>
                  <h1>Modal header</h1>
              </modal-header>
              <modal-content>
                  Press F12 see the console...press a key while modal open
              </modal-content>
              <modal-footer>
                  <button class="btn btn-primary" (click)="myModal.close()">close</button>
              </modal-footer>
          </modal>
        </ext-ngx-modal>
      </div>
    </div>
  `,
})
export class App {
  name:string;
  constructor() {
    this.name = 'Angular2'
  }
}

@NgModule({
  imports: [ BrowserModule, ModalModule ],
  declarations: [ App, NgxModalComponent ],
  exports: [ NgxModalComponent ],
  bootstrap: [ App ]
})
export class AppModule {}

Solution

  • Extending only the class - different templates

    When your NgxModalComponent component extends the Modal component, it will inherit the code like you would imaging.

    The problem is that you are overriding it's template with your own. This is a problem since some of the code you have inherited relies on the template of the original Modal component.

    Here is an example from the source code where the Modal is gaining access to an element in the template:

    /***** FROM NGX-MODAL SOURCE *****/
    @ViewChild("modalRoot")
    public modalRoot: ElementRef;
    

    When it invokes open(), it's using this reference internally to set focus on its native element:

    /***** FROM NGX-MODAL SOURCE *****/
    window.setTimeout(() => this.modalRoot.nativeElement.focus(), 0);
    

    Since you don't have a the same template and no element named modalRoot, it will fail.

    Solution(s)

    Using ContentChild (docs)

    One solution is to use ContentChild to get a reference to the Modal that is wrapped inside you template. yurzui posted a plunker showing this in this comment (yurzui created this plunker, no credit to me for that!).

    What he is doing is getting the modal reference and calling the open() method on the embedded Modal instance instead.

    @ContentChild(Modal) modal: Modal;
    
      openExt():void {
        this.modal.open();
      }
    

    Rethinking your approach

    Another option is to rethink if your approach of extending this modal is really needed and the correct way forward. But that is up to you :)

    I hope this helps!