Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Angular 8 ngx-bootstrap modal No component factory found

I have a module which looks something like:

@NgModule({
  imports: [
    EmployeeRoutingModule,
  ],
  declarations: [
    ListEmployeeComponent,
    EmployeeDialogComponent,
  ],
  providers: [
    EmployeeService,
    BsModalService,
  ],
  entryComponents: [
    EmployeeDialogComponent,
  ]
})
export class EmployeeModule {
}

And it is imported into the app module like so

{
  path: 'employee',
  loadChildren: './employee/employee.module#EmployeeModule'
},

within the app.routing.ts.

When I try and use EmployeeDialogComponent in my EmployeeModule it keeps giving off the error

ERROR Error: No component factory found for EmployeeDialogComponent. Did you add it to @NgModule.entryComponents?

If I add it to a module (let's call it SharedModule) the same way that is loaded at app run time (in the app.module.ts file) it works. It seems to be something about using it lazily.

What is the problem and how can I solve it?

Here is an example of minimal version of a modal that won't work:

import {Component, EventEmitter, OnInit, Output} from '@angular/core';
import {BsModalRef} from 'ngx-bootstrap';

@Component({
  selector: 'modal-content',
  template: `
    <div class="modal-body text-center">
      <p class="lead mb-5">{{ message }}</p>
      <button type="button" class="btn btn-lg btn-outline-secondary mr-5" (click)="decline()" >{{ btnNoText }}</button>
      <button type="button" class="btn btn-lg btn-success" (click)="confirm()" >{{ btnYesText }}</button>
    </div>
  `
})
export class ConfirmEmployeeModalComponent implements OnInit {
  @Output() result = new EventEmitter<boolean>();

  message = 'Are you sure?';
  btnNoText = 'No';
  btnYesText = 'Yes';

  constructor(public bsModalRef: BsModalRef) {
  }

  ngOnInit() {}

  confirm(): void {
    this.result.emit(true);
    this.bsModalRef.hide();
  }

  decline(): void {
    this.result.emit(false);
    this.bsModalRef.hide();
  }
}

I have a component within the module ListEmployeeComponent which contains a function like so:

import {
  Component,
  NgZone,
  ElementRef,
  OnInit,
  ViewContainerRef,
  PipeTransform,
  Pipe,
  ViewChild
} from '@angular/core';
import {FormControl} from '@angular/forms';
import {DialogService, BuiltInOptions} from "ngx-bootstrap-modal";
//import { EmployeeDialogComponent } from '../employee-dialog/employee-dialog.component';
import {EmployeeService} from "../employee.service";
import {LocalStorageUtilityService, NotificationService} from "../../common/common.services";
import * as _ from 'lodash';
//import { NotifyDialogComponent } from '../../common/dialog/notify-dialog/notify-dialog.component';
import {UserService} from "../../common/user.service";
import {ConfirmModalComponent} from "../../common/confirm-modal/confirm-modal.component";
import {BsModalService} from "ngx-bootstrap/modal";
import {EmployeeDialogComponent} from "../employee-dialog/employee-dialog.component";
import {ConfirmEmployeeModalComponent} from "../confirm-employee-modal/confirm-modal.component";

@Component({
  templateUrl: 'list-employee.component.html',
  styleUrls: ['./list-employee.component.css']
})
export class ListEmployeeComponent {
  constructor(
    private modalService: BsModalService,
    public _dialogService: DialogService,
    public _companyService: EmployeeService,
    public _notificationService: NotificationService,
    private userService: UserService,
  ) {
  }

  showAddEmployeeDialog = () => {
    this.modalService.show(ConfirmEmployeeModalComponent, {}).content.result.subscribe((details: any) => {
      }
    );
  }
}
like image 626
Sammaye Avatar asked Jul 10 '19 09:07

Sammaye


1 Answers

i managed to reproduce your issue in this repo and adding ModalModule.forRoot() to EmployeeModule imports will fixes the error.

i had a similar problem with MatDialog, such that when i wanted to use a component from a lazy loaded module in an already created instance of MatDialog (which is an injectable) i was getting the same error. Then i noticed that the service should be initialized from scratch when to be used with lazy-loaded components. And something similar is happening in your case as well.

according to docs ModalModule is imported with forRoot() like;

// RECOMMENDED
import { ModalModule } from 'ngx-bootstrap/modal';
// or
import { ModalModule } from 'ngx-bootstrap';

@NgModule({
  imports: [ModalModule.forRoot(),...]
})
export class AppModule(){}

which means a global singleton instance of BsModalService is created and used across whole app. And it doesn't have any knowledge of lazy-loaded components at initialization.

So, solution would be to initialize a new instance for lazy-loaded module such that adding ModalModule.forRoot() to EmployeeModule imports will fix the error.

@NgModule({
  imports: [
    EmployeeRoutingModule,
    ModalModule.forRoot()
  ],
  declarations: [
    EmployeeDialogComponent,
    ListEmployeeComponent,
  ],
  providers: [
    BsModalService,
  ],
  entryComponents: [
    EmployeeDialogComponent,
  ]
})
export class EmployeeModule {}

however be aware that this will create a new instance of BsModalService dedicated to EmployeeModule and it won't be aware of any other context outside of it as well as any outside context won't be aware of it. which in simpler terms means you can't interact with this particular dialog (such as closing it) anywhere else except EmployeeModule context.

like image 75
ysf Avatar answered Sep 23 '22 13:09

ysf