我有一个Angular6组件,可以动态加载另一个Angular组件:
import { Component, OnInit, OnDestroy, Input, ViewChild, ComponentFactoryResolver, Inject } from '@angular/core';
import { MatDialogRef, MAT_DIALOG_DATA } from '@angular/material';
import { CompItem } from '../../../core/pages/servicecatalog/models/comp.item.model';
import { CompComponent } from '../../../core/pages/servicecatalog/models/comp.compoent.model';
import { CompDirective } from '../../directive/comp-directive.directive';
import { CompService } from '../../services/comp.service';
import * as _ from 'lodash';
@Component({
selector: 'app-dynamic-component-holder',
templateUrl: './dynamic-component-holder.component.html',
styleUrls: ['./dynamic-component-holder.component.css']
})
export class DynamicComponentHolderComponent implements OnInit {
comp: CompItem;
@ViewChild(CompDirective) compHost: CompDirective;
customUiList: Array<CompItem> = [];
componentRef: any;
constructor(
public dialog: MatDialogRef<DynamicComponentHolderComponent>,
@Inject(MAT_DIALOG_DATA) public receivedData: any,
private componentFactoryResolver: ComponentFactoryResolver,
private _componentService: CompService
) {
}
ngOnInit() {
this.getCustomComponentList();
}
loadComponent() {
const dynamicItem = this.comp;
const componentFactory = this.componentFactoryResolver.resolveComponentFactory(dynamicItem.component);
const viewContainerRef = this.compHost.viewContainerRef;
viewContainerRef.clear();
this.componentRef = viewContainerRef.createComponent(componentFactory);
(<CompComponent>this.componentRef.instance).data = this.receivedData.data;
<CompComponent>this.componentRef.instance.
updateEmitter.subscribe((response) => {
this.dialog.close({ confirm: true, orderid: response.orderNumber, orderNumber: response.orderNumber });
});
// Default Close Emitter
<CompComponent>this.componentRef.instance.
closeEmitter.subscribe((response) => {
this.closeDialog('close');
});
}
getCustomComponentList() {
this._componentService.getComponents()
.subscribe(
(response) => {
if (response.length > 0) {
response.forEach((comp) => {
this.customUiList.push(comp);
});
const classNameIndex = _.findIndex(this.customUiList, (o) => {
return o.data.displayName == this.receivedData.data.customComponent;
});
if (classNameIndex > -1) {
this.comp = this.customUiList[classNameIndex];
this.loadComponent();
}
}
}
);
}
ngOnDestroy() {
}
closeDialog(param: any) {
<CompComponent>this.componentRef.destroy();
this.dialog.close({ confirm: false });
}
}
<ng-template comp-host></ng-template>
我正在尝试为此编写UT,但出现错误:
未捕获的TypeError:无法读取未定义的属性“ subscribe” 在DynamicComponentHolderComponent.subscribe [作为loadComponent](:9876 / _karma_webpack_ / webpack:/src/app/service-catalog/custom-ui-components/dynamic-component-holder/dynamic-component-holder.component.ts:40)< / p>
如何为动态Angular component.subscribe做UT?
请帮助。
SPECT.ts
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { DynamicComponentHolderComponent } from './dynamic-component-holder.component';
import { MatDialogModule, MatDialogRef, MAT_DIALOG_DATA } from '@angular/material';
import { CompService } from '../../services/comp.service';
import { NO_ERRORS_SCHEMA, Component, Directive } from '@angular/core';
import { of } from 'rxjs';
import { CompItem } from '../../../core/pages/servicecatalog/models/comp.item.model';
import { BrowserDynamicTestingModule } from '@angular/platform-browser-dynamic/testing';
import { CompDirective } from '../../directive/comp-directive.directive';
@Component({
selector: 'product-settings',
template: '<p>Mock Product Settings Component</p>'
})
class MockRefreshComponent {}
describe('DynamicComponentHolderComponent', () => {
let component: DynamicComponentHolderComponent;
let fixture: ComponentFixture<DynamicComponentHolderComponent>;
const MatDialogRefMock = {
close: jasmine.createSpy('close')
};
// tslint:disable-next-line:quotemark
const passedData = {"data":{"categoryIds":[{"parentId":"5c3f08a7dc57c0002e3e8506","status":"Active","_id":"5c3f08b5dc57c0002e3e8507","name":"userCat-2","description":"userCat-2","createdAt":"2019-01-16T10:34:29.280Z","updatedAt":"2019-01-16T10:34:29.280Z","__v":0}],"tags":["refresh"],"status":"Active","orderFormKey":[],"headers":[],"userGroup":[{"id":"377c866e-7b43-4c5f-87fd-461c296412df","group":"service-catalog","description":"service catalog group","isAdmin":false,"created_at":"2018-12-27T04:56:57.493Z"}],"workflow":[],"_id":"5c4662df0042bc008aa1acb7","name":"Refresh Cisco Ip Phone","description":"Refresh Cisco Ip Phone","orderFormType":"customComponent","customComponent":"Refresh Cisco IP Phone","createdAt":"2019-01-22T00:25:03.626Z","updatedAt":"2019-01-22T00:25:03.626Z","__v":0,"favourite":true,"favouriteId":"5c46b56b11d3e50064cc41f5"}};
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [
DynamicComponentHolderComponent,
MockRefreshComponent,
CompDirective
],
imports: [
MatDialogModule
],
providers: [
{ provide: MAT_DIALOG_DATA, useValue: passedData },
{ provide: MatDialogRef, useValue: MatDialogRefMock },
{ provide: CompService, useClass: MockComponentService}
],
schemas: [NO_ERRORS_SCHEMA]
});
TestBed.overrideModule(BrowserDynamicTestingModule, {
set: {
entryComponents: [MockRefreshComponent]
}
})
.compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(DynamicComponentHolderComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
it('will have the `ViewChild`s defined', () => {
expect(fixture.componentInstance.compHost).toBeDefined();
});
it('expect close Dialog to be called', () => {
const closeDialog = spyOn(component, 'closeDialog').and.callThrough();
component.closeDialog('close');
expect(closeDialog).toHaveBeenCalled();
});
});
class MockComponentService {
getComponents() {
return of([
new CompItem(MockRefreshComponent, {
'displayName': 'Refresh Cisco IP Phone',
'name': 'refreshCiscoIpPhone',
'componentName': 'refresh-cisco',
'classname': 'RefreshCiscoComponent',
'width': '90%',
'height': '95vh',
'maxWidth': '95vw',
'maxHeight': '95vh',
})
]);
}
}
export function MockDirective(options: Component): Directive {
const metadata: Directive = {
selector: options.selector,
inputs: options.inputs,
outputs: options.outputs
};
return <any>Directive(metadata)(class _ {}); // <----- add <any>
}