feat(core): add attachView / detachView to ApplicationRef

This feature is useful to allow components / embedded views
to be dirty checked if they are not placed in any `ViewContainer`.

Closes #9293
This commit is contained in:
Tobias Bosch
2016-11-04 11:58:06 -07:00
committed by Victor Berchet
parent 9de76ebfa5
commit 9f7d32a326
4 changed files with 149 additions and 29 deletions

View File

@ -21,6 +21,8 @@ import {CompilerFactory, CompilerOptions} from './linker/compiler';
import {ComponentFactory, ComponentRef} from './linker/component_factory';
import {ComponentFactoryResolver} from './linker/component_factory_resolver';
import {NgModuleFactory, NgModuleInjector, NgModuleRef} from './linker/ng_module_factory';
import {AppView} from './linker/view';
import {ViewRef, ViewRef_} from './linker/view_ref';
import {WtfScopeFn, wtfCreateScope, wtfLeave} from './profile/profile';
import {Testability, TestabilityRegistry} from './testability/testability';
import {Type} from './type';
@ -387,6 +389,23 @@ export abstract class ApplicationRef {
* Get a list of components registered to this application.
*/
get components(): ComponentRef<any>[] { return <ComponentRef<any>[]>unimplemented(); };
/**
* Attaches a view so that it will be dirty checked.
* The view will be automatically detached when it is destroyed.
* This will throw if the view is already attached to a ViewContainer.
*/
attachView(view: ViewRef): void { unimplemented(); }
/**
* Detaches a view from dirty checking again.
*/
detachView(view: ViewRef): void { unimplemented(); }
/**
* Returns the number of attached views.
*/
get viewCount() { return unimplemented(); }
}
@Injectable()
@ -397,7 +416,7 @@ export class ApplicationRef_ extends ApplicationRef {
private _bootstrapListeners: Function[] = [];
private _rootComponents: ComponentRef<any>[] = [];
private _rootComponentTypes: Type<any>[] = [];
private _changeDetectorRefs: ChangeDetectorRef[] = [];
private _views: AppView<any>[] = [];
private _runningTick: boolean = false;
private _enforceNoNewChanges: boolean = false;
@ -415,12 +434,16 @@ export class ApplicationRef_ extends ApplicationRef {
{next: () => { this._zone.run(() => { this.tick(); }); }});
}
registerChangeDetector(changeDetector: ChangeDetectorRef): void {
this._changeDetectorRefs.push(changeDetector);
attachView(viewRef: ViewRef): void {
const view = (viewRef as ViewRef_<any>).internalView;
this._views.push(view);
view.attachToAppRef(this);
}
unregisterChangeDetector(changeDetector: ChangeDetectorRef): void {
ListWrapper.remove(this._changeDetectorRefs, changeDetector);
detachView(viewRef: ViewRef): void {
const view = (viewRef as ViewRef_<any>).internalView;
ListWrapper.remove(this._views, view);
view.detach();
}
bootstrap<C>(componentOrFactory: ComponentFactory<C>|Type<C>): ComponentRef<C> {
@ -451,9 +474,8 @@ export class ApplicationRef_ extends ApplicationRef {
return compRef;
}
/** @internal */
_loadComponent(componentRef: ComponentRef<any>): void {
this._changeDetectorRefs.push(componentRef.changeDetectorRef);
private _loadComponent(componentRef: ComponentRef<any>): void {
this.attachView(componentRef.hostView);
this.tick();
this._rootComponents.push(componentRef);
// Get the listeners lazily to prevent DI cycles.
@ -463,12 +485,8 @@ export class ApplicationRef_ extends ApplicationRef {
listeners.forEach((listener) => listener(componentRef));
}
/** @internal */
_unloadComponent(componentRef: ComponentRef<any>): void {
if (this._rootComponents.indexOf(componentRef) == -1) {
return;
}
this.unregisterChangeDetector(componentRef.changeDetectorRef);
private _unloadComponent(componentRef: ComponentRef<any>): void {
this.detachView(componentRef.hostView);
ListWrapper.remove(this._rootComponents, componentRef);
}
@ -480,9 +498,9 @@ export class ApplicationRef_ extends ApplicationRef {
const scope = ApplicationRef_._tickScope();
try {
this._runningTick = true;
this._changeDetectorRefs.forEach((detector) => detector.detectChanges());
this._views.forEach((view) => view.ref.detectChanges());
if (this._enforceNoNewChanges) {
this._changeDetectorRefs.forEach((detector) => detector.checkNoChanges());
this._views.forEach((view) => view.ref.checkNoChanges());
}
} finally {
this._runningTick = false;
@ -492,9 +510,11 @@ export class ApplicationRef_ extends ApplicationRef {
ngOnDestroy() {
// TODO(alxhub): Dispose of the NgZone.
this._rootComponents.slice().forEach((component) => component.destroy());
this._views.slice().forEach((view) => view.destroy());
}
get viewCount() { return this._views.length; }
get componentTypes(): Type<any>[] { return this._rootComponentTypes; }
get components(): ComponentRef<any>[] { return this._rootComponents; }