Skip to content

Commit 21dcfc8

Browse files
committed
fix(dynamic_component_loader): implemented dispose for dynamically-loaded components
1 parent 9613772 commit 21dcfc8

File tree

4 files changed

+47
-12
lines changed

4 files changed

+47
-12
lines changed

modules/angular2/src/core/compiler/dynamic_component_loader.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ export class DynamicComponentLoader {
4343
this._viewManager.createDynamicComponentView(location, componentProtoViewRef, binding,
4444
injector);
4545
var component = this._viewManager.getComponent(location);
46-
var dispose = () => { throw new BaseException("Not implemented"); };
46+
var dispose = () => { this._viewManager.destroyDynamicComponent(location); };
4747
return new ComponentRef(location, component, dispose);
4848
});
4949
}

modules/angular2/src/core/compiler/element_injector.ts

Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -688,23 +688,21 @@ export class ElementInjector extends TreeNode<ElementInjector> {
688688
this._preBuiltObjects = null;
689689
this._lightDomAppInjector = null;
690690
this._shadowDomAppInjector = null;
691-
692691
this._strategy.callOnDestroy();
692+
this.destroyDynamicComponent();
693+
this._strategy.clearInstances();
694+
this._constructionCounter = 0;
695+
}
693696

697+
destroyDynamicComponent(): void {
694698
if (isPresent(this._dynamicallyCreatedComponentBinding) &&
695699
this._dynamicallyCreatedComponentBinding.callOnDestroy) {
696700
this._dynamicallyCreatedComponent.onDestroy();
701+
this._dynamicallyCreatedComponentBinding = null;
702+
this._dynamicallyCreatedComponent = null;
697703
}
698-
699-
this._strategy.clearInstances();
700-
701-
this._dynamicallyCreatedComponent = null;
702-
this._dynamicallyCreatedComponentBinding = null;
703-
704-
this._constructionCounter = 0;
705704
}
706705

707-
708706
hydrate(injector: Injector, host: ElementInjector, preBuiltObjects: PreBuiltObjects): void {
709707
var p = this._proto;
710708

modules/angular2/src/core/compiler/view_manager.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,14 @@ export class AppViewManager {
133133
this._destroyFreeEmbeddedView(parentView, boundElementIndex, internalView(viewRef));
134134
}
135135

136+
destroyDynamicComponent(location: ElementRef) {
137+
var hostView = internalView(location.parentView);
138+
var ei = hostView.elementInjectors[location.boundElementIndex];
139+
var componentView = hostView.componentChildViews[location.boundElementIndex];
140+
ei.destroyDynamicComponent();
141+
this._destroyComponentView(hostView, location.boundElementIndex, componentView);
142+
}
143+
136144
createViewInContainer(viewContainerLocation: ElementRef, atIndex: number,
137145
protoViewRef: ProtoViewRef, context: ElementRef = null,
138146
injector: Injector = null): ViewRef {

modules/angular2/test/core/compiler/dynamic_component_loader_spec.ts

Lines changed: 31 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ import {
1717

1818
import {TestBed, ViewProxy} from 'angular2/src/test_lib/test_bed';
1919
import {Injector} from 'angular2/di';
20-
import {Component, View} from 'angular2/annotations';
20+
import {Component, View, onDestroy} from 'angular2/annotations';
2121
import * as viewAnn from 'angular2/src/core/annotations_impl/view';
2222
import {DynamicComponentLoader} from 'angular2/src/core/compiler/dynamic_component_loader';
2323
import {ElementRef} from 'angular2/src/core/compiler/element_ref';
@@ -68,6 +68,28 @@ export function main() {
6868
});
6969
}));
7070

71+
it('should allow destroying dynamically-loaded components',
72+
inject([TestBed, AsyncTestCompleter], (tb, async) => {
73+
tb.overrideView(MyComp, new viewAnn.View({
74+
template: '<dynamic-comp #dynamic></dynamic-comp>',
75+
directives: [DynamicComp]
76+
}));
77+
78+
tb.createView(MyComp).then((view) => {
79+
var dynamicComponent = view.rawView.locals.get("dynamic");
80+
dynamicComponent.done.then((ref) => {
81+
view.detectChanges();
82+
expect(view.rootNodes).toHaveText("hello");
83+
84+
ref.dispose();
85+
86+
expect(ref.instance.destroyed).toBe(true);
87+
expect(view.rootNodes).toHaveText("");
88+
async.done();
89+
});
90+
});
91+
}));
92+
7193
it('should allow to destroy and create them via viewcontainer directives',
7294
ijTestBed((tb: TestBed, async) => {
7395
tb.overrideView(MyComp, new viewAnn.View({
@@ -287,16 +309,23 @@ class DynamicComp {
287309
}
288310
}
289311

290-
@Component({selector: 'hello-cmp', appInjector: [DynamicallyCreatedComponentService]})
312+
@Component({
313+
selector: 'hello-cmp',
314+
appInjector: [DynamicallyCreatedComponentService],
315+
lifecycle: [onDestroy]
316+
})
291317
@View({template: "{{greeting}}"})
292318
class DynamicallyCreatedCmp {
293319
greeting: string;
294320
dynamicallyCreatedComponentService: DynamicallyCreatedComponentService;
321+
destroyed: boolean = false;
295322

296323
constructor(a: DynamicallyCreatedComponentService) {
297324
this.greeting = "hello";
298325
this.dynamicallyCreatedComponentService = a;
299326
}
327+
328+
onDestroy() { this.destroyed = true; }
300329
}
301330

302331
@Component({selector: 'dummy'})

0 commit comments

Comments
 (0)