refactor(view): separate context and locals
This commit is contained in:
2
modules/angular2/src/core/application.js
vendored
2
modules/angular2/src/core/application.js
vendored
@ -67,7 +67,7 @@ function _injectorBindings(appComponentType): List<Binding> {
|
||||
// the angular application. Thus the context and lightDomInjector are
|
||||
// empty.
|
||||
var view = appProtoView.instantiate(null, eventManager);
|
||||
view.hydrate(injector, null, null, new Object());
|
||||
view.hydrate(injector, null, null, new Object(), null);
|
||||
return view;
|
||||
});
|
||||
}, [ChangeDetection, Compiler, Injector, appElementToken, appComponentAnnotatedTypeToken,
|
||||
|
@ -35,7 +35,8 @@ export class ProtoViewBuilder extends CompileStep {
|
||||
if (current.isViewRoot) {
|
||||
var protoChangeDetector = this.changeDetection.createProtoChangeDetector('dummy');
|
||||
inheritedProtoView = new ProtoView(current.element, protoChangeDetector,
|
||||
this._shadowDomStrategy);
|
||||
this._shadowDomStrategy, this._getParentProtoView(parent));
|
||||
|
||||
if (isPresent(parent)) {
|
||||
if (isPresent(parent.inheritedElementBinder.nestedProtoView)) {
|
||||
throw new BaseException('Only one nested view per element is allowed');
|
||||
@ -55,16 +56,20 @@ export class ProtoViewBuilder extends CompileStep {
|
||||
inheritedProtoView = parent.inheritedProtoView;
|
||||
}
|
||||
|
||||
// The view's contextWithLocals needs to have a full set of variable names at construction time
|
||||
// The view's locals needs to have a full set of variable names at construction time
|
||||
// in order to prevent new variables from being set later in the lifecycle. Since we don't want
|
||||
// to actually create variable bindings for the $implicit bindings, add to the
|
||||
// protoContextLocals manually.
|
||||
// protoLocals manually.
|
||||
if (isPresent(current.variableBindings)) {
|
||||
MapWrapper.forEach(current.variableBindings, (mappedName, varName) => {
|
||||
MapWrapper.set(inheritedProtoView.protoContextLocals, mappedName, null);
|
||||
MapWrapper.set(inheritedProtoView.protoLocals, mappedName, null);
|
||||
});
|
||||
}
|
||||
|
||||
current.inheritedProtoView = inheritedProtoView;
|
||||
}
|
||||
|
||||
_getParentProtoView(parent:CompileElement) {
|
||||
return isPresent(parent) ? parent.inheritedProtoView : null;
|
||||
}
|
||||
}
|
||||
|
77
modules/angular2/src/core/compiler/view.js
vendored
77
modules/angular2/src/core/compiler/view.js
vendored
@ -1,7 +1,7 @@
|
||||
import {DOM} from 'angular2/src/dom/dom_adapter';
|
||||
import {Promise} from 'angular2/src/facade/async';
|
||||
import {ListWrapper, MapWrapper, Map, StringMapWrapper, List} from 'angular2/src/facade/collection';
|
||||
import {AST, ContextWithVariableBindings, ChangeDispatcher, ProtoChangeDetector, ChangeDetector,
|
||||
import {AST, Locals, ChangeDispatcher, ProtoChangeDetector, ChangeDetector,
|
||||
ChangeRecord, BindingRecord, uninitialized} from 'angular2/change_detection';
|
||||
|
||||
import {ProtoElementInjector, ElementInjector, PreBuiltObjects} from './element_injector';
|
||||
@ -47,9 +47,9 @@ export class View {
|
||||
lightDoms: List<LightDom>;
|
||||
proto: ProtoView;
|
||||
context: any;
|
||||
contextWithLocals:ContextWithVariableBindings;
|
||||
locals:Locals;
|
||||
|
||||
constructor(proto:ProtoView, nodes:List, protoContextLocals:Map) {
|
||||
constructor(proto:ProtoView, nodes:List, protoLocals:Map) {
|
||||
this.proto = proto;
|
||||
this.nodes = nodes;
|
||||
this.changeDetector = null;
|
||||
@ -63,9 +63,7 @@ export class View {
|
||||
this.preBuiltObjects = null;
|
||||
this.lightDoms = null;
|
||||
this.context = null;
|
||||
this.contextWithLocals = (MapWrapper.size(protoContextLocals) > 0)
|
||||
? new ContextWithVariableBindings(null, MapWrapper.clone(protoContextLocals))
|
||||
: null;
|
||||
this.locals = new Locals(null, MapWrapper.clone(protoLocals)); //TODO optimize this
|
||||
}
|
||||
|
||||
init(changeDetector:ChangeDetector, elementInjectors:List, rootElementInjectors:List, textNodes: List, bindElements:List,
|
||||
@ -88,29 +86,22 @@ export class View {
|
||||
return;
|
||||
}
|
||||
var templateName = MapWrapper.get(this.proto.variableBindings, contextName);
|
||||
this.context.set(templateName, value);
|
||||
this.locals.set(templateName, value);
|
||||
}
|
||||
|
||||
hydrated() {
|
||||
return isPresent(this.context);
|
||||
}
|
||||
|
||||
_hydrateContext(newContext) {
|
||||
if (isPresent(this.contextWithLocals)) {
|
||||
this.contextWithLocals.parent = newContext;
|
||||
this.context = this.contextWithLocals;
|
||||
} else {
|
||||
this.context = newContext;
|
||||
}
|
||||
// TODO(tbosch): if we have a contextWithLocals we actually only need to
|
||||
// set the contextWithLocals once. Would it be faster to always use a contextWithLocals
|
||||
// even if we don't have locals and not update the recordRange here?
|
||||
this.changeDetector.hydrate(this.context);
|
||||
_hydrateContext(newContext, locals) {
|
||||
this.context = newContext;
|
||||
this.locals.parent = locals;
|
||||
this.changeDetector.hydrate(this.context, this.locals);
|
||||
}
|
||||
|
||||
_dehydrateContext() {
|
||||
if (isPresent(this.contextWithLocals)) {
|
||||
this.contextWithLocals.clearValues();
|
||||
if (isPresent(this.locals)) {
|
||||
this.locals.clearValues();
|
||||
}
|
||||
this.context = null;
|
||||
this.changeDetector.dehydrate();
|
||||
@ -133,9 +124,9 @@ export class View {
|
||||
* tree.
|
||||
*/
|
||||
hydrate(appInjector: Injector, hostElementInjector: ElementInjector, hostLightDom: LightDom,
|
||||
context: Object) {
|
||||
context: Object, locals:Locals) {
|
||||
if (this.hydrated()) throw new BaseException('The view is already hydrated.');
|
||||
this._hydrateContext(context);
|
||||
this._hydrateContext(context, locals);
|
||||
|
||||
// viewContainers
|
||||
for (var i = 0; i < this.viewContainers.length; i++) {
|
||||
@ -173,15 +164,15 @@ export class View {
|
||||
// name.
|
||||
var exportImplicitName = elementInjector.getExportImplicitName();
|
||||
if (elementInjector.isExportingComponent()) {
|
||||
this.context.set(exportImplicitName, elementInjector.getComponent());
|
||||
this.locals.set(exportImplicitName, elementInjector.getComponent());
|
||||
} else if (elementInjector.isExportingElement()) {
|
||||
this.context.set(exportImplicitName, elementInjector.getNgElement().domElement);
|
||||
this.locals.set(exportImplicitName, elementInjector.getNgElement().domElement);
|
||||
}
|
||||
}
|
||||
|
||||
if (isPresent(componentDirective)) {
|
||||
this.componentChildViews[componentChildViewIndex++].hydrate(shadowDomAppInjector,
|
||||
elementInjector, this.lightDoms[i], elementInjector.getComponent());
|
||||
elementInjector, this.lightDoms[i], elementInjector.getComponent(), null);
|
||||
}
|
||||
}
|
||||
|
||||
@ -294,7 +285,7 @@ export class ProtoView {
|
||||
elementBinders:List<ElementBinder>;
|
||||
protoChangeDetector:ProtoChangeDetector;
|
||||
variableBindings: Map;
|
||||
protoContextLocals:Map;
|
||||
protoLocals:Map;
|
||||
textNodesWithBindingCount:int;
|
||||
elementsWithBindingCount:int;
|
||||
instantiateInPlace:boolean;
|
||||
@ -306,16 +297,19 @@ export class ProtoView {
|
||||
// List<Map<eventName, handler>>, indexed by binder index
|
||||
eventHandlers:List;
|
||||
bindingRecords:List;
|
||||
parentProtoView:ProtoView;
|
||||
_variableBindings:List;
|
||||
|
||||
constructor(
|
||||
template,
|
||||
protoChangeDetector:ProtoChangeDetector,
|
||||
shadowDomStrategy:ShadowDomStrategy) {
|
||||
shadowDomStrategy:ShadowDomStrategy, parentProtoView:ProtoView = null) {
|
||||
this.element = template;
|
||||
this.elementBinders = [];
|
||||
this.variableBindings = MapWrapper.create();
|
||||
this.protoContextLocals = MapWrapper.create();
|
||||
this.protoLocals = MapWrapper.create();
|
||||
this.protoChangeDetector = protoChangeDetector;
|
||||
this.parentProtoView = parentProtoView;
|
||||
this.textNodesWithBindingCount = 0;
|
||||
this.elementsWithBindingCount = 0;
|
||||
this.instantiateInPlace = false;
|
||||
@ -327,6 +321,7 @@ export class ProtoView {
|
||||
this.stylePromises = [];
|
||||
this.eventHandlers = [];
|
||||
this.bindingRecords = [];
|
||||
this._variableBindings = null;
|
||||
}
|
||||
|
||||
// TODO(rado): hostElementInjector should be moved to hydrate phase.
|
||||
@ -342,6 +337,23 @@ export class ProtoView {
|
||||
}
|
||||
}
|
||||
|
||||
// this work should be done the constructor of ProtoView once we separate
|
||||
// ProtoView and ProtoViewBuilder
|
||||
_getVariableBindings() {
|
||||
if (isPresent(this._variableBindings)) {
|
||||
return this._variableBindings;
|
||||
}
|
||||
|
||||
this._variableBindings = isPresent(this.parentProtoView) ?
|
||||
ListWrapper.clone(this.parentProtoView._getVariableBindings()) : [];
|
||||
|
||||
MapWrapper.forEach(this.protoLocals, (v, local) => {
|
||||
ListWrapper.push(this._variableBindings, local);
|
||||
});
|
||||
|
||||
return this._variableBindings;
|
||||
}
|
||||
|
||||
_instantiate(hostElementInjector: ElementInjector, eventManager: EventManager): View {
|
||||
var rootElementClone = this.instantiateInPlace ? this.element : DOM.importIntoDoc(this.element);
|
||||
var elementsWithBindingsDynamic;
|
||||
@ -369,9 +381,8 @@ export class ProtoView {
|
||||
viewNodes = [rootElementClone];
|
||||
}
|
||||
|
||||
var view = new View(this, viewNodes, this.protoContextLocals);
|
||||
var changeDetector = this.protoChangeDetector.instantiate(view, this.bindingRecords);
|
||||
|
||||
var view = new View(this, viewNodes, this.protoLocals);
|
||||
var changeDetector = this.protoChangeDetector.instantiate(view, this.bindingRecords, this._getVariableBindings());
|
||||
var binders = this.elementBinders;
|
||||
var elementInjectors = ListWrapper.createFixedSize(binders.length);
|
||||
var eventHandlers = ListWrapper.createFixedSize(binders.length);
|
||||
@ -514,7 +525,7 @@ export class ProtoView {
|
||||
} else {
|
||||
context = view.elementInjectors[injectorIdx].getDirectiveAtIndex(directiveIndex);
|
||||
}
|
||||
expr.eval(new ContextWithVariableBindings(context, locals));
|
||||
expr.eval(context, new Locals(view.locals, locals));
|
||||
});
|
||||
}
|
||||
}
|
||||
@ -522,7 +533,7 @@ export class ProtoView {
|
||||
|
||||
bindVariable(contextName:string, templateName:string) {
|
||||
MapWrapper.set(this.variableBindings, contextName, templateName);
|
||||
MapWrapper.set(this.protoContextLocals, templateName, null);
|
||||
MapWrapper.set(this.protoLocals, templateName, null);
|
||||
}
|
||||
|
||||
bindElement(parent:ElementBinder, distanceToParent:int, protoElementInjector:ProtoElementInjector,
|
||||
|
@ -85,7 +85,8 @@ export class ViewContainer {
|
||||
var newView = this.defaultProtoView.instantiate(this.hostElementInjector, this._eventManager);
|
||||
// insertion must come before hydration so that element injector trees are attached.
|
||||
this.insert(newView, atIndex);
|
||||
newView.hydrate(this.appInjector, this.hostElementInjector, this.hostLightDom, this.parentView.context);
|
||||
newView.hydrate(this.appInjector, this.hostElementInjector, this.hostLightDom,
|
||||
this.parentView.context, this.parentView.locals);
|
||||
|
||||
// new content tags might have appeared, we need to redistrubute.
|
||||
if (isPresent(this.hostLightDom)) {
|
||||
|
Reference in New Issue
Block a user