X Tutup
Skip to content

Commit 1ff1792

Browse files
committed
fix(core): Unload components when individually disposed.
1 parent e6bf33e commit 1ff1792

File tree

2 files changed

+49
-9
lines changed

2 files changed

+49
-9
lines changed

modules/angular2/src/core/application_ref.ts

Lines changed: 31 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -69,10 +69,15 @@ function _componentProviders(appComponentType: Type): Array<Type | Provider | an
6969
provide(APP_COMPONENT, {useValue: appComponentType}),
7070
provide(APP_COMPONENT_REF_PROMISE,
7171
{
72-
useFactory: (dynamicComponentLoader, injector: Injector) => {
72+
useFactory: (dynamicComponentLoader: DynamicComponentLoader, appRef: ApplicationRef_,
73+
injector: Injector) => {
74+
// Save the ComponentRef for disposal later.
75+
var ref: ComponentRef;
7376
// TODO(rado): investigate whether to support providers on root component.
74-
return dynamicComponentLoader.loadAsRoot(appComponentType, null, injector)
77+
return dynamicComponentLoader.loadAsRoot(appComponentType, null, injector,
78+
() => { appRef._unloadComponent(ref); })
7579
.then((componentRef) => {
80+
ref = componentRef;
7681
if (isPresent(componentRef.location.nativeElement)) {
7782
injector.get(TestabilityRegistry)
7883
.registerApplication(componentRef.location.nativeElement,
@@ -81,7 +86,7 @@ function _componentProviders(appComponentType: Type): Array<Type | Provider | an
8186
return componentRef;
8287
});
8388
},
84-
deps: [DynamicComponentLoader, Injector]
89+
deps: [DynamicComponentLoader, ApplicationRef, Injector]
8590
}),
8691
provide(appComponentType,
8792
{
@@ -394,6 +399,10 @@ export class ApplicationRef_ extends ApplicationRef {
394399
this._changeDetectorRefs.push(changeDetector);
395400
}
396401

402+
unregisterChangeDetector(changeDetector: ChangeDetectorRef): void {
403+
ListWrapper.remove(this._changeDetectorRefs, changeDetector);
404+
}
405+
397406
bootstrap(componentType: Type,
398407
providers?: Array<Type | Provider | any[]>): Promise<ComponentRef> {
399408
var completer = PromiseWrapper.completer();
@@ -408,12 +417,8 @@ export class ApplicationRef_ extends ApplicationRef {
408417
var injector: Injector = this._injector.resolveAndCreateChild(componentProviders);
409418
var compRefToken: Promise<ComponentRef> = injector.get(APP_COMPONENT_REF_PROMISE);
410419
var tick = (componentRef) => {
411-
var appChangeDetector = internalView(componentRef.hostView).changeDetector;
412-
this._changeDetectorRefs.push(appChangeDetector.ref);
413-
this.tick();
420+
this._loadComponent(componentRef);
414421
completer.resolve(componentRef);
415-
this._rootComponents.push(componentRef);
416-
this._bootstrapListeners.forEach((listener) => listener(componentRef));
417422
};
418423

419424
var tickResult = PromiseWrapper.then(compRefToken, tick);
@@ -429,6 +434,24 @@ export class ApplicationRef_ extends ApplicationRef {
429434
return completer.promise;
430435
}
431436

437+
/** @internal */
438+
_loadComponent(ref): void {
439+
var appChangeDetector = internalView(ref.hostView).changeDetector;
440+
this._changeDetectorRefs.push(appChangeDetector.ref);
441+
this.tick();
442+
this._rootComponents.push(ref);
443+
this._bootstrapListeners.forEach((listener) => listener(ref));
444+
}
445+
446+
/** @internal */
447+
_unloadComponent(ref): void {
448+
if (!ListWrapper.contains(this._rootComponents, ref)) {
449+
return;
450+
}
451+
this.unregisterChangeDetector(internalView(ref.hostView).changeDetector.ref);
452+
ListWrapper.remove(this._rootComponents, ref);
453+
}
454+
432455
get injector(): Injector { return this._injector; }
433456

434457
get zone(): NgZone { return this._zone; }

modules/angular2/test/core/application_spec.ts

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,8 @@ import {
1212
} from 'angular2/testing_internal';
1313
import {IS_DART, isPresent, stringify} from 'angular2/src/facade/lang';
1414
import {bootstrap} from 'angular2/bootstrap';
15-
import {ApplicationRef} from 'angular2/src/core/application_ref';
15+
import {platform, applicationDomProviders} from 'angular2/src/core/application_common';
16+
import {applicationCommonProviders, ApplicationRef} from 'angular2/src/core/application_ref';
1617
import {Component, Directive, View} from 'angular2/core';
1718
import {DOM} from 'angular2/src/core/dom/dom_adapter';
1819
import {DOCUMENT} from 'angular2/render';
@@ -21,6 +22,7 @@ import {provide, Inject, Injector} from 'angular2/core';
2122
import {ExceptionHandler} from 'angular2/src/facade/exceptions';
2223
import {Testability, TestabilityRegistry} from 'angular2/src/core/testability/testability';
2324
import {ComponentRef_} from "angular2/src/core/linker/dynamic_component_loader";
25+
import {compilerProviders} from 'angular2/src/compiler/compiler';
2426

2527
@Component({selector: 'hello-app'})
2628
@View({template: '{{greeting}} world!'})
@@ -161,6 +163,21 @@ export function main() {
161163
async.done();
162164
});
163165
}));
166+
it('should unregister change detectors when components are disposed',
167+
inject([AsyncTestCompleter], (async) => {
168+
var app = platform().application([
169+
applicationCommonProviders(),
170+
applicationDomProviders(),
171+
compilerProviders(),
172+
testProviders
173+
]);
174+
app.bootstrap(HelloRootCmp)
175+
.then((ref) => {
176+
ref.dispose();
177+
expect(() => app.tick()).not.toThrow();
178+
async.done();
179+
});
180+
}));
164181

165182
it("should make the provided bindings available to the application component",
166183
inject([AsyncTestCompleter], (async) => {

0 commit comments

Comments
 (0)
X Tutup