feat(view): add `AppViewListener` interface
Basic functionality how element probe is hooked into the system.
This commit is contained in:
parent
ffb219fb91
commit
75578f41e7
|
@ -50,6 +50,7 @@ import {TestabilityRegistry, Testability} from 'angular2/src/core/testability/te
|
|||
import {AppViewPool, APP_VIEW_POOL_CAPACITY} from 'angular2/src/core/compiler/view_pool';
|
||||
import {AppViewManager} from 'angular2/src/core/compiler/view_manager';
|
||||
import {AppViewManagerUtils} from 'angular2/src/core/compiler/view_manager_utils';
|
||||
import {AppViewListener} from 'angular2/src/core/compiler/view_listener';
|
||||
import {ProtoViewFactory} from 'angular2/src/core/compiler/proto_view_factory';
|
||||
import {Renderer, RenderCompiler} from 'angular2/src/render/api';
|
||||
import {DomRenderer, DOCUMENT_TOKEN} from 'angular2/src/render/dom/dom_renderer';
|
||||
|
@ -112,6 +113,7 @@ function _injectorBindings(appComponentType): List<Type | Binding | List<any>> {
|
|||
bind(APP_VIEW_POOL_CAPACITY).toValue(10000),
|
||||
AppViewManager,
|
||||
AppViewManagerUtils,
|
||||
AppViewListener,
|
||||
Compiler,
|
||||
CompilerCache,
|
||||
TemplateResolver,
|
||||
|
|
|
@ -0,0 +1,11 @@
|
|||
import {Injectable} from 'angular2/di';
|
||||
import * as viewModule from './view';
|
||||
|
||||
/**
|
||||
* Listener for view creation / destruction.
|
||||
*/
|
||||
@Injectable()
|
||||
export class AppViewListener {
|
||||
viewCreated(view: viewModule.AppView) {}
|
||||
viewDestroyed(view: viewModule.AppView) {}
|
||||
}
|
|
@ -7,6 +7,7 @@ import {ViewContainerRef} from './view_container_ref';
|
|||
import {Renderer, RenderViewRef} from 'angular2/src/render/api';
|
||||
import {AppViewManagerUtils} from './view_manager_utils';
|
||||
import {AppViewPool} from './view_pool';
|
||||
import {AppViewListener} from './view_listener';
|
||||
|
||||
/**
|
||||
* Entry point for creating, moving views in the view hierarchy and destroying views.
|
||||
|
@ -16,13 +17,16 @@ import {AppViewPool} from './view_pool';
|
|||
@Injectable()
|
||||
export class AppViewManager {
|
||||
_viewPool: AppViewPool;
|
||||
_viewListener: AppViewListener;
|
||||
_utils: AppViewManagerUtils;
|
||||
_renderer: Renderer;
|
||||
|
||||
constructor(viewPool: AppViewPool, utils: AppViewManagerUtils, renderer: Renderer) {
|
||||
this._renderer = renderer;
|
||||
constructor(viewPool: AppViewPool, viewListener: AppViewListener, utils: AppViewManagerUtils,
|
||||
renderer: Renderer) {
|
||||
this._viewPool = viewPool;
|
||||
this._viewListener = viewListener;
|
||||
this._utils = utils;
|
||||
this._renderer = renderer;
|
||||
}
|
||||
|
||||
getComponentView(hostLocation: ElementRef): ViewRef {
|
||||
|
@ -74,6 +78,7 @@ export class AppViewManager {
|
|||
var hostView = this._utils.createView(hostProtoView, renderView, this, this._renderer);
|
||||
this._renderer.setEventDispatcher(hostView.render, hostView);
|
||||
this._createViewRecurse(hostView);
|
||||
this._viewListener.viewCreated(hostView);
|
||||
|
||||
this._utils.hydrateRootHostView(hostView, injector);
|
||||
this._viewHydrateRecurse(hostView);
|
||||
|
@ -88,6 +93,7 @@ export class AppViewManager {
|
|||
// We do want to destroy the component view though.
|
||||
this._viewDehydrateRecurse(hostView, true);
|
||||
this._renderer.destroyView(hostView.render);
|
||||
this._viewListener.viewDestroyed(hostView);
|
||||
}
|
||||
|
||||
createFreeHostView(parentComponentLocation: ElementRef, hostProtoViewRef: ProtoViewRef,
|
||||
|
@ -175,6 +181,7 @@ export class AppViewManager {
|
|||
this._renderer);
|
||||
this._renderer.setEventDispatcher(view.render, view);
|
||||
this._createViewRecurse(view);
|
||||
this._viewListener.viewCreated(view);
|
||||
}
|
||||
return view;
|
||||
}
|
||||
|
@ -192,8 +199,11 @@ export class AppViewManager {
|
|||
}
|
||||
|
||||
_destroyPooledView(view: viewModule.AppView) {
|
||||
// TODO: if the pool is full, call renderer.destroyView as well!
|
||||
this._viewPool.returnView(view);
|
||||
var wasReturned = this._viewPool.returnView(view);
|
||||
if (!wasReturned) {
|
||||
this._renderer.destroyView(view.render);
|
||||
this._viewListener.viewDestroyed(view);
|
||||
}
|
||||
}
|
||||
|
||||
_destroyViewInContainer(parentView, boundElementIndex, atIndex: number) {
|
||||
|
|
|
@ -27,15 +27,17 @@ export class AppViewPool {
|
|||
return null;
|
||||
}
|
||||
|
||||
returnView(view: viewModule.AppView) {
|
||||
returnView(view: viewModule.AppView): boolean {
|
||||
var protoView = view.proto;
|
||||
var pooledViews = MapWrapper.get(this._pooledViewsPerProtoView, protoView);
|
||||
if (isBlank(pooledViews)) {
|
||||
pooledViews = [];
|
||||
MapWrapper.set(this._pooledViewsPerProtoView, protoView, pooledViews);
|
||||
}
|
||||
if (pooledViews.length < this._poolCapacityPerProtoView) {
|
||||
var haveRemainingCapacity = pooledViews.length < this._poolCapacityPerProtoView;
|
||||
if (haveRemainingCapacity) {
|
||||
ListWrapper.push(pooledViews, view);
|
||||
}
|
||||
return haveRemainingCapacity;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -29,6 +29,7 @@ import {DirectiveResolver} from 'angular2/src/core/compiler/directive_resolver';
|
|||
import {Component} from 'angular2/annotations';
|
||||
import {AppViewManager} from 'angular2/src/core/compiler/view_manager';
|
||||
import {AppViewManagerUtils} from 'angular2/src/core/compiler/view_manager_utils';
|
||||
import {AppViewListener} from 'angular2/src/core/compiler/view_listener';
|
||||
import {AppViewPool} from 'angular2/src/core/compiler/view_pool';
|
||||
|
||||
export function main() {
|
||||
|
@ -37,6 +38,7 @@ export function main() {
|
|||
describe('AppViewManager', () => {
|
||||
var renderer;
|
||||
var utils;
|
||||
var viewListener;
|
||||
var viewPool;
|
||||
var manager;
|
||||
var directiveResolver;
|
||||
|
@ -113,8 +115,9 @@ export function main() {
|
|||
directiveResolver = new DirectiveResolver();
|
||||
renderer = new SpyRenderer();
|
||||
utils = new SpyAppViewManagerUtils();
|
||||
viewListener = new SpyAppViewListener();
|
||||
viewPool = new SpyAppViewPool();
|
||||
manager = new AppViewManager(viewPool, utils, renderer);
|
||||
manager = new AppViewManager(viewPool, viewListener, utils, renderer);
|
||||
createdViews = [];
|
||||
createdRenderViews = [];
|
||||
|
||||
|
@ -149,6 +152,7 @@ export function main() {
|
|||
ListWrapper.push(createdRenderViews, rv);
|
||||
return rv;
|
||||
});
|
||||
viewPool.spy('returnView').andReturn(true);
|
||||
});
|
||||
|
||||
describe('createDynamicComponentView', () => {
|
||||
|
@ -165,6 +169,7 @@ export function main() {
|
|||
elementRef(wrapView(hostView), 0), wrapPv(componentProtoView), null, null)))
|
||||
.toBe(createdViews[0]);
|
||||
expect(createdViews[0].proto).toBe(componentProtoView);
|
||||
expect(viewListener.spy('viewCreated')).toHaveBeenCalledWith(createdViews[0]);
|
||||
});
|
||||
|
||||
it('should get the view from the pool', () => {
|
||||
|
@ -178,6 +183,7 @@ export function main() {
|
|||
.toBe(createdView);
|
||||
expect(utils.spy('createView')).not.toHaveBeenCalled();
|
||||
expect(renderer.spy('createView')).not.toHaveBeenCalled();
|
||||
expect(viewListener.spy('viewCreated')).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should attach the view', () => {
|
||||
|
@ -315,6 +321,7 @@ export function main() {
|
|||
wrapPv(hostProtoView), null)))
|
||||
.toBe(createdViews[0]);
|
||||
expect(createdViews[0].proto).toBe(hostProtoView);
|
||||
expect(viewListener.spy('viewCreated')).toHaveBeenCalledWith(createdViews[0]);
|
||||
});
|
||||
|
||||
it('should attachAndHydrate the view', () => {
|
||||
|
@ -377,7 +384,16 @@ export function main() {
|
|||
it('should return the view to the pool', () => {
|
||||
manager.destroyFreeHostView(elementRef(wrapView(parentHostView), 0), wrapView(hostView));
|
||||
expect(viewPool.spy('returnView')).toHaveBeenCalledWith(hostView);
|
||||
expect(renderer.spy('destroyView')).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should destroy the view if the pool is full', () => {
|
||||
viewPool.spy('returnView').andReturn(false);
|
||||
manager.destroyFreeHostView(elementRef(wrapView(parentHostView), 0), wrapView(hostView));
|
||||
expect(renderer.spy('destroyView')).toHaveBeenCalledWith(hostView.render);
|
||||
expect(viewListener.spy('viewDestroyed')).toHaveBeenCalledWith(hostView);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('recursively destroy inPlaceHostViews', () => {
|
||||
|
@ -395,6 +411,7 @@ export function main() {
|
|||
expect(internalView(manager.createRootHostView(wrapPv(hostProtoView), null, null)))
|
||||
.toBe(createdViews[0]);
|
||||
expect(createdViews[0].proto).toBe(hostProtoView);
|
||||
expect(viewListener.spy('viewCreated')).toHaveBeenCalledWith(createdViews[0]);
|
||||
});
|
||||
|
||||
it('should hydrate the view', () => {
|
||||
|
@ -444,6 +461,7 @@ export function main() {
|
|||
it('should destroy the render view', () => {
|
||||
manager.destroyRootHostView(wrapView(hostView));
|
||||
expect(renderer.spy('destroyView')).toHaveBeenCalledWith(hostRenderViewRef);
|
||||
expect(viewListener.spy('viewDestroyed')).toHaveBeenCalledWith(hostView);
|
||||
});
|
||||
|
||||
it('should not return the view to the pool', () => {
|
||||
|
@ -473,6 +491,7 @@ export function main() {
|
|||
wrapPv(childProtoView), null)))
|
||||
.toBe(createdViews[0]);
|
||||
expect(createdViews[0].proto).toBe(childProtoView);
|
||||
expect(viewListener.spy('viewCreated')).toHaveBeenCalledWith(createdViews[0]);
|
||||
});
|
||||
|
||||
it('should attach the view', () => {
|
||||
|
@ -622,6 +641,13 @@ class SpyAppViewManagerUtils extends SpyObject {
|
|||
noSuchMethod(m) { return super.noSuchMethod(m) }
|
||||
}
|
||||
|
||||
@proxy
|
||||
@IMPLEMENTS(AppViewListener)
|
||||
class SpyAppViewListener extends SpyObject {
|
||||
constructor() { super(AppViewListener); }
|
||||
noSuchMethod(m) { return super.noSuchMethod(m) }
|
||||
}
|
||||
|
||||
@proxy
|
||||
@IMPLEMENTS(ElementInjector)
|
||||
class SpyElementInjector extends SpyObject {
|
||||
|
|
|
@ -58,9 +58,9 @@ export function main() {
|
|||
var view1 = createView(pv);
|
||||
var view2 = createView(pv);
|
||||
var view3 = createView(pv);
|
||||
vf.returnView(view1);
|
||||
vf.returnView(view2);
|
||||
vf.returnView(view3);
|
||||
expect(vf.returnView(view1)).toBe(true);
|
||||
expect(vf.returnView(view2)).toBe(true);
|
||||
expect(vf.returnView(view3)).toBe(false);
|
||||
|
||||
expect(vf.getView(pv)).toBe(view2);
|
||||
expect(vf.getView(pv)).toBe(view1);
|
||||
|
|
Loading…
Reference in New Issue