Skip to content
Snippets Groups Projects
Commit 5b14cfce authored by Xiao Gui's avatar Xiao Gui
Browse files

fixes #816 #744

parent 84e77b3f
No related branches found
No related tags found
No related merge requests found
......@@ -135,7 +135,8 @@
<ng-template [ngIf]="matExpansionPanel.expanded">
<feature-container
[feature]="feature"
[region]="region$ | async">
[region]="region$ | async"
(viewChanged)="detectChange()">
</feature-container>
</ng-template>
</mat-expansion-panel>
......
import { CommonModule } from "@angular/common"
import { ChangeDetectorRef, Component, ComponentRef, EventEmitter, NgModule } from "@angular/core"
import { async, TestBed } from "@angular/core/testing"
import { By } from "@angular/platform-browser"
import { RegionalFeaturesService } from "../regionalFeature.service"
import { ISingleFeature } from "../singleFeatures/interfaces"
import { FeatureContainer } from "./featureContainer.component"
const dummyCmpType = 'dummyType'
@Component({
template: `{{ text }}`
})
class DummyComponent implements ISingleFeature{
text = 'hello world'
feature: any
region: any
viewChanged = new EventEmitter<boolean>()
}
@Component({
template: ''
})
class HostCmp{
public feature: any
public region: any
constructor(public cdr: ChangeDetectorRef){
}
detectChange(){
this.cdr.detectChanges()
}
}
const serviceStub = {
mapFeatToCmp: new Map([
[dummyCmpType, DummyComponent]
])
}
@NgModule({
declarations: [
FeatureContainer,
DummyComponent,
],
entryComponents: [
DummyComponent
],
providers: [
{
provide: RegionalFeaturesService,
useValue: serviceStub
}
],
exports: [
FeatureContainer,
DummyComponent,
]
})
class DummyModule{}
describe('> featureContainer.component.ts', () => {
describe('> FeatureContainer', () => {
beforeEach(async(() => {
TestBed.configureTestingModule({
imports: [
CommonModule,
DummyModule,
],
declarations: [
HostCmp,
],
}).overrideComponent(HostCmp, {
set: {
template: `
<feature-container
[feature]="feature"
[region]="region"
(viewChanged)="detectChange()">
</feature-container>`
}
}).compileComponents()
}))
it('> can be created', () => {
const fixture = TestBed.createComponent(HostCmp)
expect(fixture).toBeTruthy()
const featContainer = fixture.debugElement.query(By.directive(FeatureContainer))
expect(featContainer).toBeTruthy()
})
describe('> if inputs change', () => {
it('> if input changed, but feature is not one of them, map.get will not be called', () => {
const fixture = TestBed.createComponent(HostCmp)
// const featContainer = fixture.debugElement.query(By.directive(FeatureContainer))
spyOn(serviceStub.mapFeatToCmp, 'get').and.callThrough()
fixture.componentInstance.region = {
name: 'tesla'
}
fixture.detectChanges()
expect(serviceStub.mapFeatToCmp.get).not.toHaveBeenCalled()
})
it('> if input changed, feature is one of them, will not call map.get', () => {
const fixture = TestBed.createComponent(HostCmp)
const dummyFeature = {
type: dummyCmpType
}
spyOn(serviceStub.mapFeatToCmp, 'get').and.callThrough()
fixture.componentInstance.feature = dummyFeature
fixture.detectChanges()
expect(serviceStub.mapFeatToCmp.get).toHaveBeenCalledWith(dummyCmpType)
})
it('> should render default txt', () => {
const fixture = TestBed.createComponent(HostCmp)
const dummyFeature = {
type: dummyCmpType
}
fixture.componentInstance.feature = dummyFeature
fixture.detectChanges()
const text = fixture.nativeElement.textContent
expect(text).toContain('hello world')
})
it('> if inner component changes, if view changed does not emit, will not change ui', () => {
const fixture = TestBed.createComponent(HostCmp)
const dummyFeature = {
type: dummyCmpType
}
fixture.componentInstance.feature = dummyFeature
fixture.detectChanges()
const featureContainer = fixture.debugElement.query(
By.directive(FeatureContainer)
)
const cr = (featureContainer.componentInstance as FeatureContainer)['cr'] as ComponentRef<DummyComponent>
cr.instance.text = 'foo bar'
const text = fixture.nativeElement.textContent
expect(text).toContain('hello world')
})
it('> if inner component changes, and viewChanged is emitted, ui should change accordingly', () => {
const fixture = TestBed.createComponent(HostCmp)
const dummyFeature = {
type: dummyCmpType
}
fixture.componentInstance.feature = dummyFeature
fixture.detectChanges()
const featureContainer = fixture.debugElement.query(
By.directive(FeatureContainer)
)
const cr = (featureContainer.componentInstance as FeatureContainer)['cr'] as ComponentRef<DummyComponent>
cr.instance.text = 'foo bar'
cr.instance.viewChanged.emit(true)
const text = fixture.nativeElement.textContent
expect(text).toContain('foo bar')
})
})
})
})
import { ChangeDetectionStrategy, ChangeDetectorRef, Component, ComponentFactoryResolver, ComponentRef, Input, OnChanges, SimpleChanges, ViewContainerRef } from "@angular/core";
import { ChangeDetectionStrategy, ChangeDetectorRef, Component, ComponentFactoryResolver, ComponentRef, Input, OnChanges, Output, SimpleChanges, ViewContainerRef, EventEmitter } from "@angular/core";
import { Subscription } from "rxjs";
import { IFeature, RegionalFeaturesService } from "../regionalFeature.service";
import { ISingleFeature } from "../singleFeatures/interfaces";
......@@ -16,13 +16,15 @@ export class FeatureContainer implements OnChanges{
@Input()
region: any
@Output()
viewChanged: EventEmitter<boolean> = new EventEmitter()
private cr: ComponentRef<ISingleFeature>
constructor(
private vCRef: ViewContainerRef,
private rService: RegionalFeaturesService,
private cfr: ComponentFactoryResolver,
private cdr: ChangeDetectorRef
){
}
......@@ -33,15 +35,22 @@ export class FeatureContainer implements OnChanges{
const { currentValue, previousValue } = simpleChanges.feature
if (currentValue === previousValue) return
this.clear()
/**
* catching instances where currentValue for feature is falsy
*/
if (!currentValue) return
/**
* TODO catch if map is undefined
*/
const comp = this.rService.mapFeatToCmp.get(currentValue.type)
if (!comp) throw new Error(`mapFeatToCmp for ${currentValue.type} not defined`)
const cf = this.cfr.resolveComponentFactory<ISingleFeature>(comp)
this.cr = this.vCRef.createComponent(cf)
this.cr.instance.feature = this.feature
this.cr.instance.region = this.region
this.viewChangedSub = this.cr.instance.viewChanged.subscribe(() => this.cdr.detectChanges())
this.viewChangedSub = this.cr.instance.viewChanged.subscribe(() => this.viewChanged.emit(true))
}
clear(){
......
......@@ -31,7 +31,7 @@ export class IEEGRecordingsCmp extends RegionFeatureBase implements ISingleFeatu
super(regionFeatureService)
}
public viewChanged = new EventEmitter<null>()
public viewChanged = new EventEmitter<boolean>()
ngOnInit(){
if (this.regClickIntp) {
......@@ -71,7 +71,7 @@ export class IEEGRecordingsCmp extends RegionFeatureBase implements ISingleFeatu
)
this.sub.push(
this.dataIsLoading$.subscribe(() => this.viewChanged.emit(null))
this.dataIsLoading$.subscribe(() => this.viewChanged.emit(true))
)
this.onDestroyCb.push(() => {
......
......@@ -4,5 +4,5 @@ import { IFeature } from "../regionalFeature.service";
export interface ISingleFeature{
feature: IFeature
region: any
viewChanged: EventEmitter<null>
viewChanged: EventEmitter<boolean>
}
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment