我有一个类似的组件
<select #tabSelect (change)="tabLoad($event.target.value)" class="mr-2">
<option value="tab1">First tab</option>
<option value="tab2">Second tab</option>
</select>
<div class="tab-content">
<div class="tab-pane fade show active">
<ng-template #tabContent></ng-template>
</div>
</div>
有两个选项卡,它们调用tabLoad((函数并发送参数单击了哪个选项卡。
export class DemoComponent implements OnInit {
@ViewChild('tabContent', { read: ViewContainerRef }) entry: ViewContainerRef;
activeTab: any = 'tab1';
constructor(private resolver: ComponentFactoryResolver) { }
ngOnInit() {
this.tabLoad(this.activeTab);
}
tabLoad(page) {
setTimeout(() => {
this.activeTab = page;
this.entry.clear();
if (page == 'tab1') {
const factory = this.resolver.resolveComponentFactory(Tab1Component);
console.log(this.entry);
this.entry.createComponent(factory);
} else if (page == 'tab2') {
const factory = this.resolver.resolveComponentFactory(Tab2Component);
this.entry.createComponent(factory);
}
}, 500);
}
}
在这个.tsfle中,我创建了一个名为entry的变量,它指向#tabContent.Tab内容加载组件取决于哪个页面是活动的。
我为这种行为写了一个测试套件,如下
fdescribe('DemoComponent', () => {
let component: DemoComponent;
let fixture: ComponentFixture<DemoComponent>;
beforeEach(async(() => {
TestBed.configureTestingModule({
imports: [RouterModule.forRoot([]), SharedModule],
declarations: [Tab1Component, Tab2Component],
}).compileComponents().then(() => {
fixture = TestBed.createComponent(DemoComponent);
component = fixture.componentInstance;
});
}));
it('should set activeTab correctly and clear entry when tabLoad is called', fakeAsync(() => {
component.tabLoad("tab1");
flush();
expect(component.activeTab).toBe('tab1');
}));
});
当我调用This.entry.clear((;时,此测试失败并显示Cannot read property 'clear' of undefined
。此外,console.log(this.entry);
打印未定义。然后我决定在上添加fixture.detectChanges()
。compileComponents().then(() => {})
作用域,但仍以相同方式失败。但当我在发球后转到页面时,一切都很好。
第一个fixture.detectChanges()
调用ngOnInit
,您需要在fakeAsync
区域中调用它才能使flush
生效,因为setTimeout
是在ngOnInit
调用上创建的。本质上,计时器需要在fakeAsync
区域中创建。因此,尝试将fixture.detectChanges()
放入测试(it块(中,并将其从.then
中移除。
beforeEach(async(() => {
TestBed.configureTestingModule({
imports: [RouterModule.forRoot([]), SharedModule],
declarations: [Tab1Component, Tab2Component],
}).compileComponents().then(() => {
fixture = TestBed.createComponent(DemoComponent);
component = fixture.componentInstance;
// !! remove fixture.detectChanges() from here
});
}));
it('should set activeTab correctly and clear entry when tabLoad is called', fakeAsync(() => {
// !! add it here
fixture.detectChanges();
component.tabLoad("tab1");
flush();
expect(component.activeTab).toBe('tab1');
}));
正如Petr
所说,您可以使用ngAfterViewInit
而不是使用500ms
。
试试这个:
export class DemoComponent implements OnInit, AfterViewInit { /* add AfterViewInit */
@ViewChild('tabContent', { read: ViewContainerRef }) entry: ViewContainerRef;
activeTab: any = 'tab1';
constructor(private resolver: ComponentFactoryResolver) { }
ngOnInit() {
}
ngAfterViewInit() {
this.tabLoad(this.activeTab);
}
tabLoad(page) {
this.activeTab = page;
this.entry.clear();
if (page == 'tab1') {
const factory = this.resolver.resolveComponentFactory(Tab1Component);
console.log(this.entry);
this.entry.createComponent(factory);
} else if (page == 'tab2') {
const factory = this.resolver.resolveComponentFactory(Tab2Component);
this.entry.createComponent(factory);
}
}
}
然后我认为你不需要fakeAsync
和flush
。