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