Skip to content

Commit f830cfc

Browse files
committed
refactor(view): provide ViewContainers dynamically on any element
1 parent eac5c88 commit f830cfc

26 files changed

Lines changed: 467 additions & 210 deletions

modules/angular2/src/core/application.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -105,8 +105,8 @@ function _injectorBindings(appComponentType): List<Binding> {
105105
// TODO(tbosch): We need an explicit factory here, as
106106
// we are getting errors in dart2js with mirrors...
107107
bind(ViewFactory).toFactory(
108-
(capacity, renderer) => new ViewFactory(capacity, renderer),
109-
[VIEW_POOL_CAPACITY, Renderer]
108+
(capacity, renderer, appViewHydrator) => new ViewFactory(capacity, renderer, appViewHydrator),
109+
[VIEW_POOL_CAPACITY, Renderer, AppViewHydrator]
110110
),
111111
bind(VIEW_POOL_CAPACITY).toValue(10000),
112112
AppViewHydrator,

modules/angular2/src/core/compiler/element_injector.js

Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,10 @@ export class ElementRef {
3535
return this.elementInjector._preBuiltObjects.view;
3636
}
3737

38+
get viewContainer() {
39+
return this.hostView.getOrCreateViewContainer(this.boundElementIndex);
40+
}
41+
3842
get injector() {
3943
return this.elementInjector._lightDomAppInjector;
4044
}
@@ -298,13 +302,10 @@ export class DirectiveBinding extends ResolvedBinding {
298302
export class PreBuiltObjects {
299303
view:viewModule.AppView;
300304
element:NgElement;
301-
viewContainer:ViewContainer;
302305
changeDetector:ChangeDetector;
303-
constructor(view, element:NgElement, viewContainer:ViewContainer,
304-
changeDetector:ChangeDetector) {
306+
constructor(view, element:NgElement, changeDetector:ChangeDetector) {
305307
this.view = view;
306308
this.element = element;
307-
this.viewContainer = viewContainer;
308309
this.changeDetector = changeDetector;
309310
}
310311
}
@@ -929,7 +930,7 @@ export class ElementInjector extends TreeNode {
929930
// TODO: AppView should not be injectable. Remove it.
930931
if (keyId === staticKeys.viewId) return this._preBuiltObjects.view;
931932
if (keyId === staticKeys.ngElementId) return this._preBuiltObjects.element;
932-
if (keyId === staticKeys.viewContainerId) return this._preBuiltObjects.viewContainer;
933+
if (keyId === staticKeys.viewContainerId) return this._preBuiltObjects.view.getOrCreateViewContainer(this._proto.index);
933934
if (keyId === staticKeys.changeDetectorRefId) return this._preBuiltObjects.changeDetector.ref;
934935

935936
//TODO add other objects as needed
@@ -984,6 +985,18 @@ export class ElementInjector extends TreeNode {
984985
getExportImplicitName() {
985986
return this._proto.exportImplicitName;
986987
}
988+
989+
getLightDomAppInjector() {
990+
return this._lightDomAppInjector;
991+
}
992+
993+
getHost() {
994+
return this._host;
995+
}
996+
997+
getBoundElementIndex() {
998+
return this._proto.index;
999+
}
9871000
}
9881001

9891002
class OutOfBoundsAccess extends Error {

modules/angular2/src/core/compiler/view.js

Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ import {SetterFn} from 'angular2/src/reflection/types';
88
import {IMPLEMENTS, int, isPresent, isBlank, BaseException} from 'angular2/src/facade/lang';
99
import {ViewContainer} from './view_container';
1010
import * as renderApi from 'angular2/src/render/api';
11+
import * as vfModule from './view_factory';
12+
import * as vhModule from './view_hydrator';
1113

1214
/**
1315
* Const of making objects: http://jsperf.com/instantiate-size-of-object
@@ -17,7 +19,6 @@ import * as renderApi from 'angular2/src/render/api';
1719
// TODO(tbosch): this is not supported in dart2js (no '.' is allowed)
1820
// @IMPLEMENTS(renderApi.EventDispatcher)
1921
export class AppView {
20-
2122
render:renderApi.ViewRef;
2223
/// This list matches the _nodes list. It is sparse, since only Elements have ElementInjector
2324
rootElementInjectors:List<ElementInjector>;
@@ -28,6 +29,8 @@ export class AppView {
2829
preBuiltObjects: List<PreBuiltObjects>;
2930
proto: AppProtoView;
3031
renderer: renderApi.Renderer;
32+
viewFactory: vfModule.ViewFactory;
33+
viewHydrator: vhModule.AppViewHydrator;
3134

3235
/**
3336
* The context against which data-binding expressions in this view are evaluated against.
@@ -43,30 +46,40 @@ export class AppView {
4346
*/
4447
locals:Locals;
4548

46-
constructor(renderer:renderApi.Renderer, proto:AppProtoView, protoLocals:Map) {
49+
constructor(renderer:renderApi.Renderer, viewFactory:vfModule.ViewFactory, viewHydrator:vhModule.AppViewHydrator, proto:AppProtoView, protoLocals:Map) {
4750
this.render = null;
4851
this.proto = proto;
4952
this.changeDetector = null;
5053
this.elementInjectors = null;
5154
this.rootElementInjectors = null;
5255
this.componentChildViews = null;
53-
this.viewContainers = null;
56+
this.viewContainers = ListWrapper.createFixedSize(this.proto.elementBinders.length);
5457
this.preBuiltObjects = null;
5558
this.context = null;
5659
this.locals = new Locals(null, MapWrapper.clone(protoLocals)); //TODO optimize this
5760
this.renderer = renderer;
61+
this.viewFactory = viewFactory;
62+
this.viewHydrator = viewHydrator;
5863
}
5964

6065
init(changeDetector:ChangeDetector, elementInjectors:List, rootElementInjectors:List,
61-
viewContainers:List, preBuiltObjects:List, componentChildViews:List) {
66+
preBuiltObjects:List, componentChildViews:List) {
6267
this.changeDetector = changeDetector;
6368
this.elementInjectors = elementInjectors;
6469
this.rootElementInjectors = rootElementInjectors;
65-
this.viewContainers = viewContainers;
6670
this.preBuiltObjects = preBuiltObjects;
6771
this.componentChildViews = componentChildViews;
6872
}
6973

74+
getOrCreateViewContainer(boundElementIndex:number) {
75+
var viewContainer = this.viewContainers[boundElementIndex];
76+
if (isBlank(viewContainer)) {
77+
viewContainer = new ViewContainer(this, this.proto.elementBinders[boundElementIndex].nestedProtoView, this.elementInjectors[boundElementIndex]);
78+
this.viewContainers[boundElementIndex] = viewContainer;
79+
}
80+
return viewContainer;
81+
}
82+
7083
setLocal(contextName: string, value) {
7184
if (!this.hydrated()) throw new BaseException('Cannot set locals on dehydrated view.');
7285
if (!MapWrapper.contains(this.proto.variableBindings, contextName)) {

modules/angular2/src/core/compiler/view_container.js

Lines changed: 14 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -4,45 +4,31 @@ import {Injector} from 'angular2/di';
44
import * as eiModule from 'angular2/src/core/compiler/element_injector';
55
import {isPresent, isBlank} from 'angular2/src/facade/lang';
66

7-
import * as renderApi from 'angular2/src/render/api';
87
import * as viewModule from './view';
9-
import * as vfModule from './view_factory';
10-
import * as vhModule from './view_hydrator';
11-
import {Renderer} from 'angular2/src/render/api';
8+
import {ViewContainerRef} from 'angular2/src/render/api';
129

1310
/**
1411
* @exportedAs angular2/view
1512
*/
1613
export class ViewContainer {
17-
viewFactory: vfModule.ViewFactory;
18-
viewHydrator: vhModule.AppViewHydrator;
19-
renderer: Renderer;
20-
21-
render:renderApi.ViewContainerRef;
2214
parentView: viewModule.AppView;
2315
defaultProtoView: viewModule.AppProtoView;
2416
_views: List<viewModule.AppView>;
2517
elementInjector: eiModule.ElementInjector;
26-
appInjector: Injector;
27-
hostElementInjector: eiModule.ElementInjector;
2818

29-
constructor(viewFactory:vfModule.ViewFactory,
30-
renderer: Renderer,
31-
parentView: viewModule.AppView,
19+
constructor(parentView: viewModule.AppView,
3220
defaultProtoView: viewModule.AppProtoView,
3321
elementInjector: eiModule.ElementInjector) {
34-
this.viewFactory = viewFactory;
35-
this.viewHydrator = null;
36-
this.renderer = renderer;
37-
this.render = null;
3822
this.parentView = parentView;
3923
this.defaultProtoView = defaultProtoView;
4024
this.elementInjector = elementInjector;
4125

4226
// The order in this list matches the DOM order.
4327
this._views = [];
44-
this.appInjector = null;
45-
this.hostElementInjector = null;
28+
}
29+
30+
getRender() {
31+
return new ViewContainerRef(this.parentView.render, this.elementInjector.getBoundElementIndex());
4632
}
4733

4834
internalClearWithoutRender() {
@@ -71,22 +57,22 @@ export class ViewContainer {
7157
}
7258

7359
hydrated() {
74-
return isPresent(this.appInjector);
60+
return this.parentView.hydrated();
7561
}
7662

7763
// TODO(rado): profile and decide whether bounds checks should be added
7864
// to the methods below.
79-
create(atIndex=-1, protoView:viewModule.AppProtoView = null): viewModule.AppView {
65+
create(atIndex=-1, protoView:viewModule.AppProtoView = null, injector:Injector = null): viewModule.AppView {
8066
if (atIndex == -1) atIndex = this._views.length;
8167
if (!this.hydrated()) throw new BaseException(
8268
'Cannot create views on a dehydrated ViewContainer');
8369
if (isBlank(protoView)) {
8470
protoView = this.defaultProtoView;
8571
}
86-
var newView = this.viewFactory.getView(protoView);
72+
var newView = this.parentView.viewFactory.getView(protoView);
8773
// insertion must come before hydration so that element injector trees are attached.
8874
this._insertInjectors(newView, atIndex);
89-
this.viewHydrator.hydrateViewInViewContainer(this, atIndex, newView);
75+
this.parentView.viewHydrator.hydrateViewInViewContainer(this, atIndex, newView, injector);
9076

9177
return newView;
9278
}
@@ -95,7 +81,7 @@ export class ViewContainer {
9581
if (atIndex == -1) atIndex = this._views.length;
9682
this._insertInjectors(view, atIndex);
9783
this.parentView.changeDetector.addChild(view.changeDetector);
98-
this.renderer.insertViewIntoContainer(this.render, atIndex, view.render);
84+
this.parentView.renderer.insertViewIntoContainer(this.getRender(), atIndex, view.render);
9985
return view;
10086
}
10187

@@ -110,9 +96,9 @@ export class ViewContainer {
11096
if (atIndex == -1) atIndex = this._views.length - 1;
11197
var view = this._views[atIndex];
11298
// opposite order as in create
113-
this.viewHydrator.dehydrateViewInViewContainer(this, atIndex, view);
99+
this.parentView.viewHydrator.dehydrateViewInViewContainer(this, atIndex, view);
114100
this._detachInjectors(atIndex);
115-
this.viewFactory.returnView(view);
101+
this.parentView.viewFactory.returnView(view);
116102
// view is intentionally not returned to the client.
117103
}
118104

@@ -124,7 +110,7 @@ export class ViewContainer {
124110
if (atIndex == -1) atIndex = this._views.length - 1;
125111
var detachedView = this._detachInjectors(atIndex);
126112
detachedView.changeDetector.remove();
127-
this.renderer.detachViewFromContainer(this.render, atIndex);
113+
this.parentView.renderer.detachViewFromContainer(this.getRender(), atIndex);
128114
return detachedView;
129115
}
130116

modules/angular2/src/core/compiler/view_factory.js

Lines changed: 7 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,9 @@ import {ListWrapper, MapWrapper, Map, StringMapWrapper, List} from 'angular2/src
33
import * as eli from './element_injector';
44
import {isPresent, isBlank, BaseException} from 'angular2/src/facade/lang';
55
import {NgElement} from 'angular2/src/core/compiler/ng_element';
6-
import * as vcModule from './view_container';
76
import * as viewModule from './view';
87
import {Renderer} from 'angular2/src/render/api';
8+
import {AppViewHydrator} from './view_hydrator';
99

1010
// TODO(tbosch): Make this an OpaqueToken as soon as our transpiler supports this!
1111
export const VIEW_POOL_CAPACITY = 'ViewFactory.viewPoolCapacity';
@@ -15,11 +15,13 @@ export class ViewFactory {
1515
_poolCapacityPerProtoView:number;
1616
_pooledViewsPerProtoView:Map<viewModule.AppProtoView, List<viewModule.AppView>>;
1717
_renderer:Renderer;
18+
_viewHydrator:AppViewHydrator;
1819

19-
constructor(@Inject(VIEW_POOL_CAPACITY) poolCapacityPerProtoView, renderer:Renderer) {
20+
constructor(@Inject(VIEW_POOL_CAPACITY) poolCapacityPerProtoView, renderer:Renderer, viewHydrator:AppViewHydrator) {
2021
this._poolCapacityPerProtoView = poolCapacityPerProtoView;
2122
this._pooledViewsPerProtoView = MapWrapper.create();
2223
this._renderer = renderer;
24+
this._viewHydrator = viewHydrator;
2325
}
2426

2527
getView(protoView:viewModule.AppProtoView):viewModule.AppView {
@@ -50,15 +52,14 @@ export class ViewFactory {
5052
}
5153

5254
_createView(protoView:viewModule.AppProtoView): viewModule.AppView {
53-
var view = new viewModule.AppView(this._renderer, protoView, protoView.protoLocals);
55+
var view = new viewModule.AppView(this._renderer, this, this._viewHydrator, protoView, protoView.protoLocals);
5456
var changeDetector = protoView.protoChangeDetector.instantiate(view, protoView.bindings,
5557
protoView.getVariableBindings(), protoView.getdirectiveRecords());
5658

5759
var binders = protoView.elementBinders;
5860
var elementInjectors = ListWrapper.createFixedSize(binders.length);
5961
var rootElementInjectors = [];
6062
var preBuiltObjects = ListWrapper.createFixedSize(binders.length);
61-
var viewContainers = ListWrapper.createFixedSize(binders.length);
6263
var componentChildViews = ListWrapper.createFixedSize(binders.length);
6364

6465
for (var binderIdx = 0; binderIdx < binders.length; binderIdx++) {
@@ -88,22 +89,14 @@ export class ViewFactory {
8889
componentChildViews[binderIdx] = childView;
8990
}
9091

91-
// viewContainers
92-
var viewContainer = null;
93-
if (isPresent(binder.viewportDirective)) {
94-
viewContainer = new vcModule.ViewContainer(this, this._renderer, view, binder.nestedProtoView, elementInjector);
95-
}
96-
viewContainers[binderIdx] = viewContainer;
97-
9892
// preBuiltObjects
9993
if (isPresent(elementInjector)) {
100-
preBuiltObjects[binderIdx] = new eli.PreBuiltObjects(view, new NgElement(view, binderIdx), viewContainer,
101-
childChangeDetector);
94+
preBuiltObjects[binderIdx] = new eli.PreBuiltObjects(view, new NgElement(view, binderIdx), childChangeDetector);
10295
}
10396
}
10497

10598
view.init(changeDetector, elementInjectors, rootElementInjectors,
106-
viewContainers, preBuiltObjects, componentChildViews);
99+
preBuiltObjects, componentChildViews);
107100

108101
return view;
109102
}

modules/angular2/src/core/compiler/view_hydrator.js

Lines changed: 8 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -44,8 +44,7 @@ export class AppViewHydrator {
4444
}
4545
var hostElementInjector = hostView.elementInjectors[boundElementIndex];
4646
if (isBlank(injector)) {
47-
// TODO: We should have another way of accesing the app injector at hostView place.
48-
injector = new eli.ElementRef(hostElementInjector).injector;
47+
injector = hostElementInjector.getLightDomAppInjector();
4948
}
5049

5150
// shadowDomAppInjector
@@ -114,19 +113,22 @@ export class AppViewHydrator {
114113
this._renderer.destroyInPlaceHostView(parentRenderViewRef, render);
115114
}
116115

117-
hydrateViewInViewContainer(viewContainer:vcModule.ViewContainer, atIndex:number, view:viewModule.AppView) {
116+
hydrateViewInViewContainer(viewContainer:vcModule.ViewContainer, atIndex:number, view:viewModule.AppView, injector:Injector = null) {
118117
if (!viewContainer.hydrated()) throw new BaseException(
119118
'Cannot create views on a dehydrated ViewContainer');
120-
var renderViewRefs = this._renderer.createViewInContainer(viewContainer.render, atIndex, view.proto.render);
119+
if (isBlank(injector)) {
120+
injector = viewContainer.elementInjector.getLightDomAppInjector();
121+
}
122+
var renderViewRefs = this._renderer.createViewInContainer(viewContainer.getRender(), atIndex, view.proto.render);
121123
viewContainer.parentView.changeDetector.addChild(view.changeDetector);
122-
this._viewHydrateRecurse(view, renderViewRefs, 0, viewContainer.appInjector, viewContainer.hostElementInjector,
124+
this._viewHydrateRecurse(view, renderViewRefs, 0, injector, viewContainer.elementInjector.getHost(),
123125
viewContainer.parentView.context, viewContainer.parentView.locals);
124126
}
125127

126128
dehydrateViewInViewContainer(viewContainer:vcModule.ViewContainer, atIndex:number, view:viewModule.AppView) {
127129
view.changeDetector.remove();
128130
this._viewDehydrateRecurse(view);
129-
this._renderer.destroyViewInContainer(viewContainer.render, atIndex);
131+
this._renderer.destroyViewInContainer(viewContainer.getRender(), atIndex);
130132
}
131133

132134
_viewHydrateRecurse(
@@ -142,14 +144,6 @@ export class AppViewHydrator {
142144
view.context = context;
143145
view.locals.parent = locals;
144146

145-
// viewContainers
146-
for (var i = 0; i < view.viewContainers.length; i++) {
147-
var vc = view.viewContainers[i];
148-
if (isPresent(vc)) {
149-
this._viewContainerHydrateRecurse(vc, new renderApi.ViewContainerRef(view.render, i), appInjector, hostElementInjector);
150-
}
151-
}
152-
153147
var binders = view.proto.elementBinders;
154148
for (var i = 0; i < binders.length; ++i) {
155149
var componentDirective = binders[i].componentDirective;
@@ -273,16 +267,6 @@ export class AppViewHydrator {
273267
return shadowDomAppInjector;
274268
}
275269

276-
/**
277-
* This should only be called by View or ViewContainer.
278-
*/
279-
_viewContainerHydrateRecurse(viewContainer:vcModule.ViewContainer, render:renderApi.ViewContainerRef, appInjector: Injector, hostElementInjector: eli.ElementInjector) {
280-
viewContainer.viewHydrator = this;
281-
viewContainer.render = render;
282-
viewContainer.appInjector = appInjector;
283-
viewContainer.hostElementInjector = hostElementInjector;
284-
}
285-
286270
/**
287271
* This should only be called by View or ViewContainer.
288272
*/
@@ -296,10 +280,6 @@ export class AppViewHydrator {
296280
// as we don't want to change the render side
297281
// as the render side does its own recursion.
298282
viewContainer.internalClearWithoutRender();
299-
viewContainer.viewHydrator = null;
300-
viewContainer.appInjector = null;
301-
viewContainer.hostElementInjector = null;
302-
viewContainer.render = null;
303283
}
304284

305285
}

0 commit comments

Comments
 (0)