Angular 未找到未定义的组件工厂。您是否将其添加到@NgModule.entryComponents?
我正在为angular 8应用程序编写一个jasmine测试,测试单击事件是否调用了onOverlayClicked方法。在运行测试时,我得到了错误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
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();
});
});