Skip to content

Commit 75578f4

Browse files
committed
feat(view): add AppViewListener interface
Basic functionality how element probe is hooked into the system.
1 parent ffb219f commit 75578f4

File tree

6 files changed

+61
-10
lines changed

6 files changed

+61
-10
lines changed

modules/angular2/src/core/application.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ import {TestabilityRegistry, Testability} from 'angular2/src/core/testability/te
5050
import {AppViewPool, APP_VIEW_POOL_CAPACITY} from 'angular2/src/core/compiler/view_pool';
5151
import {AppViewManager} from 'angular2/src/core/compiler/view_manager';
5252
import {AppViewManagerUtils} from 'angular2/src/core/compiler/view_manager_utils';
53+
import {AppViewListener} from 'angular2/src/core/compiler/view_listener';
5354
import {ProtoViewFactory} from 'angular2/src/core/compiler/proto_view_factory';
5455
import {Renderer, RenderCompiler} from 'angular2/src/render/api';
5556
import {DomRenderer, DOCUMENT_TOKEN} from 'angular2/src/render/dom/dom_renderer';
@@ -112,6 +113,7 @@ function _injectorBindings(appComponentType): List<Type | Binding | List<any>> {
112113
bind(APP_VIEW_POOL_CAPACITY).toValue(10000),
113114
AppViewManager,
114115
AppViewManagerUtils,
116+
AppViewListener,
115117
Compiler,
116118
CompilerCache,
117119
TemplateResolver,
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
import {Injectable} from 'angular2/di';
2+
import * as viewModule from './view';
3+
4+
/**
5+
* Listener for view creation / destruction.
6+
*/
7+
@Injectable()
8+
export class AppViewListener {
9+
viewCreated(view: viewModule.AppView) {}
10+
viewDestroyed(view: viewModule.AppView) {}
11+
}

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

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import {ViewContainerRef} from './view_container_ref';
77
import {Renderer, RenderViewRef} from 'angular2/src/render/api';
88
import {AppViewManagerUtils} from './view_manager_utils';
99
import {AppViewPool} from './view_pool';
10+
import {AppViewListener} from './view_listener';
1011

1112
/**
1213
* Entry point for creating, moving views in the view hierarchy and destroying views.
@@ -16,13 +17,16 @@ import {AppViewPool} from './view_pool';
1617
@Injectable()
1718
export class AppViewManager {
1819
_viewPool: AppViewPool;
20+
_viewListener: AppViewListener;
1921
_utils: AppViewManagerUtils;
2022
_renderer: Renderer;
2123

22-
constructor(viewPool: AppViewPool, utils: AppViewManagerUtils, renderer: Renderer) {
23-
this._renderer = renderer;
24+
constructor(viewPool: AppViewPool, viewListener: AppViewListener, utils: AppViewManagerUtils,
25+
renderer: Renderer) {
2426
this._viewPool = viewPool;
27+
this._viewListener = viewListener;
2528
this._utils = utils;
29+
this._renderer = renderer;
2630
}
2731

2832
getComponentView(hostLocation: ElementRef): ViewRef {
@@ -74,6 +78,7 @@ export class AppViewManager {
7478
var hostView = this._utils.createView(hostProtoView, renderView, this, this._renderer);
7579
this._renderer.setEventDispatcher(hostView.render, hostView);
7680
this._createViewRecurse(hostView);
81+
this._viewListener.viewCreated(hostView);
7782

7883
this._utils.hydrateRootHostView(hostView, injector);
7984
this._viewHydrateRecurse(hostView);
@@ -88,6 +93,7 @@ export class AppViewManager {
8893
// We do want to destroy the component view though.
8994
this._viewDehydrateRecurse(hostView, true);
9095
this._renderer.destroyView(hostView.render);
96+
this._viewListener.viewDestroyed(hostView);
9197
}
9298

9399
createFreeHostView(parentComponentLocation: ElementRef, hostProtoViewRef: ProtoViewRef,
@@ -175,6 +181,7 @@ export class AppViewManager {
175181
this._renderer);
176182
this._renderer.setEventDispatcher(view.render, view);
177183
this._createViewRecurse(view);
184+
this._viewListener.viewCreated(view);
178185
}
179186
return view;
180187
}
@@ -192,8 +199,11 @@ export class AppViewManager {
192199
}
193200

194201
_destroyPooledView(view: viewModule.AppView) {
195-
// TODO: if the pool is full, call renderer.destroyView as well!
196-
this._viewPool.returnView(view);
202+
var wasReturned = this._viewPool.returnView(view);
203+
if (!wasReturned) {
204+
this._renderer.destroyView(view.render);
205+
this._viewListener.viewDestroyed(view);
206+
}
197207
}
198208

199209
_destroyViewInContainer(parentView, boundElementIndex, atIndex: number) {

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

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,15 +27,17 @@ export class AppViewPool {
2727
return null;
2828
}
2929

30-
returnView(view: viewModule.AppView) {
30+
returnView(view: viewModule.AppView): boolean {
3131
var protoView = view.proto;
3232
var pooledViews = MapWrapper.get(this._pooledViewsPerProtoView, protoView);
3333
if (isBlank(pooledViews)) {
3434
pooledViews = [];
3535
MapWrapper.set(this._pooledViewsPerProtoView, protoView, pooledViews);
3636
}
37-
if (pooledViews.length < this._poolCapacityPerProtoView) {
37+
var haveRemainingCapacity = pooledViews.length < this._poolCapacityPerProtoView;
38+
if (haveRemainingCapacity) {
3839
ListWrapper.push(pooledViews, view);
3940
}
41+
return haveRemainingCapacity;
4042
}
4143
}

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

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ import {DirectiveResolver} from 'angular2/src/core/compiler/directive_resolver';
2929
import {Component} from 'angular2/annotations';
3030
import {AppViewManager} from 'angular2/src/core/compiler/view_manager';
3131
import {AppViewManagerUtils} from 'angular2/src/core/compiler/view_manager_utils';
32+
import {AppViewListener} from 'angular2/src/core/compiler/view_listener';
3233
import {AppViewPool} from 'angular2/src/core/compiler/view_pool';
3334

3435
export function main() {
@@ -37,6 +38,7 @@ export function main() {
3738
describe('AppViewManager', () => {
3839
var renderer;
3940
var utils;
41+
var viewListener;
4042
var viewPool;
4143
var manager;
4244
var directiveResolver;
@@ -113,8 +115,9 @@ export function main() {
113115
directiveResolver = new DirectiveResolver();
114116
renderer = new SpyRenderer();
115117
utils = new SpyAppViewManagerUtils();
118+
viewListener = new SpyAppViewListener();
116119
viewPool = new SpyAppViewPool();
117-
manager = new AppViewManager(viewPool, utils, renderer);
120+
manager = new AppViewManager(viewPool, viewListener, utils, renderer);
118121
createdViews = [];
119122
createdRenderViews = [];
120123

@@ -149,6 +152,7 @@ export function main() {
149152
ListWrapper.push(createdRenderViews, rv);
150153
return rv;
151154
});
155+
viewPool.spy('returnView').andReturn(true);
152156
});
153157

154158
describe('createDynamicComponentView', () => {
@@ -165,6 +169,7 @@ export function main() {
165169
elementRef(wrapView(hostView), 0), wrapPv(componentProtoView), null, null)))
166170
.toBe(createdViews[0]);
167171
expect(createdViews[0].proto).toBe(componentProtoView);
172+
expect(viewListener.spy('viewCreated')).toHaveBeenCalledWith(createdViews[0]);
168173
});
169174

170175
it('should get the view from the pool', () => {
@@ -178,6 +183,7 @@ export function main() {
178183
.toBe(createdView);
179184
expect(utils.spy('createView')).not.toHaveBeenCalled();
180185
expect(renderer.spy('createView')).not.toHaveBeenCalled();
186+
expect(viewListener.spy('viewCreated')).not.toHaveBeenCalled();
181187
});
182188

183189
it('should attach the view', () => {
@@ -315,6 +321,7 @@ export function main() {
315321
wrapPv(hostProtoView), null)))
316322
.toBe(createdViews[0]);
317323
expect(createdViews[0].proto).toBe(hostProtoView);
324+
expect(viewListener.spy('viewCreated')).toHaveBeenCalledWith(createdViews[0]);
318325
});
319326

320327
it('should attachAndHydrate the view', () => {
@@ -377,7 +384,16 @@ export function main() {
377384
it('should return the view to the pool', () => {
378385
manager.destroyFreeHostView(elementRef(wrapView(parentHostView), 0), wrapView(hostView));
379386
expect(viewPool.spy('returnView')).toHaveBeenCalledWith(hostView);
387+
expect(renderer.spy('destroyView')).not.toHaveBeenCalled();
380388
});
389+
390+
it('should destroy the view if the pool is full', () => {
391+
viewPool.spy('returnView').andReturn(false);
392+
manager.destroyFreeHostView(elementRef(wrapView(parentHostView), 0), wrapView(hostView));
393+
expect(renderer.spy('destroyView')).toHaveBeenCalledWith(hostView.render);
394+
expect(viewListener.spy('viewDestroyed')).toHaveBeenCalledWith(hostView);
395+
});
396+
381397
});
382398

383399
describe('recursively destroy inPlaceHostViews', () => {
@@ -395,6 +411,7 @@ export function main() {
395411
expect(internalView(manager.createRootHostView(wrapPv(hostProtoView), null, null)))
396412
.toBe(createdViews[0]);
397413
expect(createdViews[0].proto).toBe(hostProtoView);
414+
expect(viewListener.spy('viewCreated')).toHaveBeenCalledWith(createdViews[0]);
398415
});
399416

400417
it('should hydrate the view', () => {
@@ -444,6 +461,7 @@ export function main() {
444461
it('should destroy the render view', () => {
445462
manager.destroyRootHostView(wrapView(hostView));
446463
expect(renderer.spy('destroyView')).toHaveBeenCalledWith(hostRenderViewRef);
464+
expect(viewListener.spy('viewDestroyed')).toHaveBeenCalledWith(hostView);
447465
});
448466

449467
it('should not return the view to the pool', () => {
@@ -473,6 +491,7 @@ export function main() {
473491
wrapPv(childProtoView), null)))
474492
.toBe(createdViews[0]);
475493
expect(createdViews[0].proto).toBe(childProtoView);
494+
expect(viewListener.spy('viewCreated')).toHaveBeenCalledWith(createdViews[0]);
476495
});
477496

478497
it('should attach the view', () => {
@@ -622,6 +641,13 @@ class SpyAppViewManagerUtils extends SpyObject {
622641
noSuchMethod(m) { return super.noSuchMethod(m) }
623642
}
624643

644+
@proxy
645+
@IMPLEMENTS(AppViewListener)
646+
class SpyAppViewListener extends SpyObject {
647+
constructor() { super(AppViewListener); }
648+
noSuchMethod(m) { return super.noSuchMethod(m) }
649+
}
650+
625651
@proxy
626652
@IMPLEMENTS(ElementInjector)
627653
class SpyElementInjector extends SpyObject {

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

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -58,9 +58,9 @@ export function main() {
5858
var view1 = createView(pv);
5959
var view2 = createView(pv);
6060
var view3 = createView(pv);
61-
vf.returnView(view1);
62-
vf.returnView(view2);
63-
vf.returnView(view3);
61+
expect(vf.returnView(view1)).toBe(true);
62+
expect(vf.returnView(view2)).toBe(true);
63+
expect(vf.returnView(view3)).toBe(false);
6464

6565
expect(vf.getView(pv)).toBe(view2);
6666
expect(vf.getView(pv)).toBe(view1);

0 commit comments

Comments
 (0)