Angular 未找到未定义的组件工厂。您是否将其添加到@NgModule.entryComponents?

Angular 未找到未定义的组件工厂。您是否将其添加到@NgModule.entryComponents?,angular,jasmine,Angular,Jasmine,我正在为angular 8应用程序编写一个jasmine测试,测试单击事件是否调用了onOverlayClicked方法。在运行测试时,我得到了错误 Error: No component factory found for undefined. Did you add it to @NgModule.entryComponents? error properties: Object({ ngComponent: undefined, ngDebugContext: DebugC

我正在为angular 8应用程序编写一个jasmine测试,测试单击事件是否调用了onOverlayClicked方法。在运行测试时,我得到了错误

Error: No component factory found for undefined. Did you add it to @NgModule.entryComponents?
        error properties: Object({ ngComponent: undefined, ngDebugContext: DebugContext_({ view: Object({ def: Object({ factory: Function, nodeFlags: 37928961, rootNodeFlags: 33554433, nodeMatchedQueries: 0, flags: 0, nodes: [ Object({ nodeIndex: 0, parent: null, renderParent: null, bindingIndex: 0, outputIndex: 0, checkIndex: 0, flags: 33554433, childFlags: 4374528, directChildFlags: 4374528, childMatchedQueries: 0, matchedQueries: Object({  }), matchedQueryIds: 0, references: Object({  }), ngContentIndex: null, childCount: 1, bindings: [  ], bindingFlags: 0, outputs: [  ], element: 
Object({ ns: '', name: 'app-dialog', attrs: [  ], template: null, componentProvider: Object({ nodeIndex: 1, parent: <circular reference: Object>, renderParent: <circular reference: Object>, bindingIndex: 0, outputIndex: 0, checkIndex: 1, flags: 4374528, childFlags: 0, directChildFlags: 0, childMatchedQueries: 0, matchedQueries: Object, matchedQueryIds: 0, references: Object, ngContentIndex: 
-1, childCount: 0, bindings: Array, b ...
我不确定是什么问题

模块

import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { DialogComponent } from '../../components/dialog/dialog.component';
import { InsertionDirective } from '../../shared/directives/insertion.directive';

@NgModule({
  imports: [CommonModule],
  declarations: [DialogComponent, InsertionDirective],
  entryComponents: [DialogComponent]
})

export class DialogModule {}
组成部分

import { AfterViewInit, ChangeDetectorRef, Component, ComponentFactoryResolver, ComponentRef, OnDestroy, Type, ViewChild } from '@angular/core';
import { Subject } from 'rxjs';
import { InsertionDirective } from '../../shared/directives/insertion.directive';
import { DialogRef } from './config/dialog-ref';

@Component({
  selector: 'app-dialog',
  templateUrl: './dialog.component.html'
})

export class DialogComponent implements AfterViewInit, OnDestroy {
  private readonly _onClose = new Subject<any>();

  public componentRef: ComponentRef<any>;
  public childComponentType: Type<any>;
  public onClose = this._onClose.asObservable();

  // add this:
  @ViewChild(InsertionDirective, { static: false })
  insertionPoint: InsertionDirective;

  constructor(public componentFactoryResolver: ComponentFactoryResolver,
              public cd: ChangeDetectorRef,
              public dialog: DialogRef) {
    console.log('My InsertionPoint');
    console.log(this.insertionPoint);
  }

  ngAfterViewInit() {
    this.loadChildComponent(this.childComponentType);
    this.cd.detectChanges();
  }

  ngOnDestroy() {
    if (this.componentRef) {
      this.componentRef.destroy();
    }
  }

  onOverlayClicked(evt: MouseEvent) {
    // close the dialog
  }

  onDialogClicked(evt: MouseEvent) {
    evt.stopPropagation();
  }

  loadChildComponent(componentType: Type<any>) {
    const componentFactory = this.componentFactoryResolver.resolveComponentFactory(componentType);

    const viewContainerRef = this.insertionPoint.viewContainerRef;
    console.log(viewContainerRef);
    viewContainerRef.clear();

    this.componentRef = viewContainerRef.createComponent(componentFactory);
  }

  closeModal() {
    this.dialog.close();
  }
}

在DialogComponent类中,您正在使用ComponentResolver以编程方式创建角度组件,如下面的代码段所示:

ngAfterViewInit() {
  this.loadChildComponent(this.childComponentType);
  this.cd.detectChanges();
}

loadChildComponent(componentType: Type<any>) {
  const componentFactory = this.componentFactoryResolver.resolveComponentFactory(componentType);

  const viewContainerRef = this.insertionPoint.viewContainerRef;
  console.log(viewContainerRef);
  viewContainerRef.clear();

  this.componentRef = viewContainerRef.createComponent(componentFactory);
}

这将立即解决您的错误。请注意,您必须在entryComponents数组中包含XYZComponent,以便在运行时解析组件。

Hi Tom,这可能是您将指令导入对话框组件并同时使用viewchild的方式吗?您是否尝试删除这些引用并查看是否可以进一步进行测试,因为您所做的只是将其记录到控制台?否如果您在loadChildComponent方法中看到,我将使用它获取viewContainerRef。在我的示例中,它的对话组件是入口点。它已添加到beforeeach中的overridemodule声明中。正如你们所示,我在每一秒之前分配了对话组件,但仍然得到同样的错误,我有点困惑。如果我尝试添加当前属于appmodule一部分的xyz组件,我会收到一个错误,说它不是ngmodule的一部分。同样如您所述,它不允许我在您所示的位置添加EntryComponent:[XYZComponent]。我已将其添加到overridemodule声明中。已删除对话框组件并将其替换为xyzcomponent。我做得对吗?我终于成功了。我做了一些调整。谢谢
describe('DialogComponent', () => {
  let component: DialogComponent;
  let fixture: ComponentFixture<DialogComponent>;

  beforeEach(async(() => {
    TestBed.configureTestingModule({
      imports: [SharedModule, DialogModule], // DialogModule
      //  declarations: [ InsertionDirective ],
      providers: [ DialogRef ]
    })
    .overrideModule(BrowserDynamicTestingModule, { set: { entryComponents: [DialogComponent] } })
    .compileComponents();
  }));

  beforeEach(() => {
    fixture = TestBed.createComponent(DialogComponent);
    component = fixture.componentInstance;
    fixture.detectChanges();
  });

  it('should create', () => {
    expect(component).toBeTruthy();
  });

  fit('should set call onOverlayClicked click', () => {
   // const fixture = TestBed.createComponent(AppComponent);
    fixture.detectChanges();

    spyOn(component, 'onOverlayClicked');
    const overlay = fixture.debugElement.query(By.css('.overlay'));
    overlay.triggerEventHandler('click', {});
    fixture.detectChanges();

    expect(component.onOverlayClicked).toHaveBeenCalled();

  });
});
import { Directive, ViewContainerRef } from '@angular/core';

@Directive({
  selector: '[appInsertion]',
})
export class InsertionDirective {
  constructor(public viewContainerRef: ViewContainerRef) {}
}
ngAfterViewInit() {
  this.loadChildComponent(this.childComponentType);
  this.cd.detectChanges();
}

loadChildComponent(componentType: Type<any>) {
  const componentFactory = this.componentFactoryResolver.resolveComponentFactory(componentType);

  const viewContainerRef = this.insertionPoint.viewContainerRef;
  console.log(viewContainerRef);
  viewContainerRef.clear();

  this.componentRef = viewContainerRef.createComponent(componentFactory);
}
describe('DialogComponent', () => {
  let component: DialogComponent;
  let fixture: ComponentFixture<DialogComponent>;

  beforeEach(async(() => {
    TestBed.configureTestingModule({
      imports: [SharedModule, DialogModule], // DialogModule
      //  declarations: [ InsertionDirective ],
      /*
      * Since you want to programatically create an instance of XYZComponent
      * DialogComponent, include XYZComponent in entryComponents array
      */
      entryComponents: [XYZComponent],
      providers: [ DialogRef ]
    })
    .overrideModule(BrowserDynamicTestingModule, { set: { entryComponents: [DialogComponent] } })
    .compileComponents();
  }));

  beforeEach(() => {
    fixture = TestBed.createComponent(DialogComponent);
    component = fixture.componentInstance;
    // initialize childComponentType object
    component.childComponentType = XYZComponent;
    fixture.detectChanges();
  });

  it('should create', () => {
    expect(component).toBeTruthy();
  });

  fit('should set call onOverlayClicked click', () => {
    spyOn(component, 'onOverlayClicked');
    const overlay = fixture.debugElement.query(By.css('.overlay'));
    overlay.triggerEventHandler('click', {});
    fixture.detectChanges();

    expect(component.onOverlayClicked).toHaveBeenCalled();
  });
});