refactor(render): use render layer fully
Introduces angular2/src/core/compiler/ViewFactory which extracts ProtoView.instantiate and replaces ViewPool. Note: This is a work in progress commit to unblock other commits. There will be follow ups to add unit tests, remove TODOs, …
This commit is contained in:
6
modules/angular2/src/render/api.js
vendored
6
modules/angular2/src/render/api.js
vendored
@ -203,7 +203,7 @@ export class Renderer {
|
||||
* Sets the dispatcher for all events that have been defined in the template or in directives
|
||||
* in the given view.
|
||||
*/
|
||||
setEventDispatcher(viewRef:ViewRef, dispatcher:EventDispatcher):void {}
|
||||
setEventDispatcher(viewRef:ViewRef, dispatcher:any/*EventDispatcher*/):void {}
|
||||
|
||||
/**
|
||||
* To be called at the end of the VmTurn so the API can buffer calls
|
||||
@ -218,10 +218,10 @@ export class Renderer {
|
||||
export class EventDispatcher {
|
||||
/**
|
||||
* Called when an event was triggered for a on-* attribute on an element.
|
||||
* @param {List<any>} locals Locals to be used to evaluate the
|
||||
* @param {Map<string, any>} locals Locals to be used to evaluate the
|
||||
* event expressions
|
||||
*/
|
||||
dispatchEvent(
|
||||
elementIndex:number, eventName:string, locals:List<any>
|
||||
elementIndex:number, eventName:string, locals:Map<string, any>
|
||||
):void {}
|
||||
}
|
||||
|
@ -1,10 +1,14 @@
|
||||
import {Injectable} from 'angular2/di';
|
||||
|
||||
import {PromiseWrapper, Promise} from 'angular2/src/facade/async';
|
||||
import {BaseException} from 'angular2/src/facade/lang';
|
||||
|
||||
import {Template, ProtoView} from '../../api';
|
||||
import {CompilePipeline} from './compile_pipeline';
|
||||
import {TemplateLoader} from 'angular2/src/render/dom/compiler/template_loader';
|
||||
import {CompileStepFactory} from './compile_step_factory';
|
||||
import {CompileStepFactory, DefaultStepFactory} from './compile_step_factory';
|
||||
import {Parser} from 'angular2/change_detection';
|
||||
import {ShadowDomStrategy} from '../shadow_dom/shadow_dom_strategy';
|
||||
|
||||
/**
|
||||
* The compiler loads and translates the html templates of components into
|
||||
@ -43,4 +47,11 @@ export class Compiler {
|
||||
return PromiseWrapper.resolve(protoView);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Injectable()
|
||||
export class DefaultCompiler extends Compiler {
|
||||
constructor(parser:Parser, shadowDomStrategy:ShadowDomStrategy, templateLoader: TemplateLoader) {
|
||||
super(new DefaultStepFactory(parser, shadowDomStrategy), templateLoader);
|
||||
}
|
||||
}
|
@ -1,3 +1,4 @@
|
||||
import {Injectable} from 'angular2/di';
|
||||
import {Promise, PromiseWrapper} from 'angular2/src/facade/async';
|
||||
import {List, ListWrapper} from 'angular2/src/facade/collection';
|
||||
import {isBlank, isPresent} from 'angular2/src/facade/lang';
|
||||
@ -14,7 +15,7 @@ function _resolveViewContainer(vc:api.ViewContainerRef) {
|
||||
return _resolveView(vc.view).viewContainers[vc.elementIndex];
|
||||
}
|
||||
|
||||
function _resolveView(viewRef:_DirectDomViewRef) {
|
||||
function _resolveView(viewRef:DirectDomViewRef) {
|
||||
return isPresent(viewRef) ? viewRef.delegate : null;
|
||||
}
|
||||
|
||||
@ -23,7 +24,7 @@ function _resolveProtoView(protoViewRef:DirectDomProtoViewRef) {
|
||||
}
|
||||
|
||||
function _wrapView(view:View) {
|
||||
return new _DirectDomViewRef(view);
|
||||
return new DirectDomViewRef(view);
|
||||
}
|
||||
|
||||
function _collectComponentChildViewRefs(view, target = null) {
|
||||
@ -51,7 +52,7 @@ export class DirectDomProtoViewRef extends api.ProtoViewRef {
|
||||
}
|
||||
}
|
||||
|
||||
class _DirectDomViewRef extends api.ViewRef {
|
||||
export class DirectDomViewRef extends api.ViewRef {
|
||||
delegate:View;
|
||||
|
||||
constructor(delegate:View) {
|
||||
@ -60,6 +61,7 @@ class _DirectDomViewRef extends api.ViewRef {
|
||||
}
|
||||
}
|
||||
|
||||
@Injectable()
|
||||
export class DirectDomRenderer extends api.Renderer {
|
||||
_compiler: Compiler;
|
||||
_viewFactory: ViewFactory;
|
||||
@ -131,7 +133,7 @@ export class DirectDomRenderer extends api.Renderer {
|
||||
_resolveView(viewRef).setText(textNodeIndex, text);
|
||||
}
|
||||
|
||||
setEventDispatcher(viewRef:api.ViewRef, dispatcher:api.EventDispatcher) {
|
||||
setEventDispatcher(viewRef:api.ViewRef, dispatcher:any/*api.EventDispatcher*/):void {
|
||||
_resolveView(viewRef).setEventDispatcher(dispatcher);
|
||||
}
|
||||
}
|
||||
|
@ -242,19 +242,19 @@ export class EventLocalsAstSplitter extends AstTransformer {
|
||||
}
|
||||
|
||||
splitEventAstIntoLocals(eventBindings:Map<string, ASTWithSource>):Map<string, ASTWithSource> {
|
||||
// TODO(tbosch): reenable this when we are using
|
||||
// the render views
|
||||
return eventBindings;
|
||||
// if (isPresent(eventBindings)) {
|
||||
// var result = MapWrapper.create();
|
||||
// MapWrapper.forEach(eventBindings, (astWithSource, eventName) => {
|
||||
// var adjustedAst = astWithSource.ast.visit(this);
|
||||
// MapWrapper.set(result, eventName, new ASTWithSource(adjustedAst, astWithSource.source, ''));
|
||||
// ListWrapper.push(this.eventNames, eventName);
|
||||
// });
|
||||
// return result;
|
||||
// }
|
||||
// return null;
|
||||
if (isPresent(eventBindings)) {
|
||||
var result = MapWrapper.create();
|
||||
MapWrapper.forEach(eventBindings, (astWithSource, eventName) => {
|
||||
// TODO(tbosch): reenable this when we are parsing element properties
|
||||
// out of action expressions
|
||||
// var adjustedAst = astWithSource.ast.visit(this);
|
||||
var adjustedAst = astWithSource.ast;
|
||||
MapWrapper.set(result, eventName, new ASTWithSource(adjustedAst, astWithSource.source, ''));
|
||||
ListWrapper.push(this.eventNames, eventName);
|
||||
});
|
||||
return result;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
visitAccessMember(ast:AccessMember) {
|
||||
|
21
modules/angular2/src/render/dom/view/view.js
vendored
21
modules/angular2/src/render/dom/view/view.js
vendored
@ -1,7 +1,6 @@
|
||||
import {DOM} from 'angular2/src/dom/dom_adapter';
|
||||
import {ListWrapper, MapWrapper, Map, StringMapWrapper, List} from 'angular2/src/facade/collection';
|
||||
import {int, isPresent, isBlank, BaseException} from 'angular2/src/facade/lang';
|
||||
import {Locals} from 'angular2/change_detection';
|
||||
|
||||
import {ViewContainer} from './view_container';
|
||||
import {ProtoView} from './proto_view';
|
||||
@ -10,7 +9,7 @@ import {Content} from '../shadow_dom/content_tag';
|
||||
|
||||
import {ShadowDomStrategy} from '../shadow_dom/shadow_dom_strategy';
|
||||
|
||||
import {EventDispatcher} from '../../api';
|
||||
// import {EventDispatcher} from '../../api';
|
||||
|
||||
const NG_BINDING_CLASS = 'ng-binding';
|
||||
|
||||
@ -31,7 +30,7 @@ export class View {
|
||||
lightDoms: List<LightDom>;
|
||||
proto: ProtoView;
|
||||
_hydrated: boolean;
|
||||
_eventDispatcher: EventDispatcher;
|
||||
_eventDispatcher: any/*EventDispatcher*/;
|
||||
|
||||
constructor(
|
||||
proto:ProtoView, rootNodes:List,
|
||||
@ -43,6 +42,7 @@ export class View {
|
||||
this.viewContainers = viewContainers;
|
||||
this.contentTags = contentTags;
|
||||
this.lightDoms = ListWrapper.createFixedSize(boundElements.length);
|
||||
ListWrapper.fill(this.lightDoms, null);
|
||||
this.componentChildViews = ListWrapper.createFixedSize(boundElements.length);
|
||||
this._hydrated = false;
|
||||
}
|
||||
@ -134,7 +134,10 @@ export class View {
|
||||
|
||||
// componentChildViews
|
||||
for (var i = 0; i < this.componentChildViews.length; i++) {
|
||||
this.componentChildViews[i].dehydrate();
|
||||
var cv = this.componentChildViews[i];
|
||||
if (isPresent(cv)) {
|
||||
cv.dehydrate();
|
||||
}
|
||||
}
|
||||
|
||||
// viewContainers and content tags
|
||||
@ -150,10 +153,11 @@ export class View {
|
||||
}
|
||||
}
|
||||
}
|
||||
this._eventDispatcher = null;
|
||||
this._hydrated = false;
|
||||
}
|
||||
|
||||
setEventDispatcher(dispatcher:EventDispatcher) {
|
||||
setEventDispatcher(dispatcher:any/*EventDispatcher*/) {
|
||||
this._eventDispatcher = dispatcher;
|
||||
}
|
||||
|
||||
@ -161,8 +165,11 @@ export class View {
|
||||
if (isPresent(this._eventDispatcher)) {
|
||||
var evalLocals = MapWrapper.create();
|
||||
MapWrapper.set(evalLocals, '$event', event);
|
||||
var localValues = this.proto.elementBinders[elementIndex].eventLocals.eval(null, new Locals(null, evalLocals));
|
||||
this._eventDispatcher.dispatchEvent(elementIndex, eventName, localValues);
|
||||
// TODO(tbosch): reenable this when we are parsing element properties
|
||||
// out of action expressions
|
||||
// var localValues = this.proto.elementBinders[elementIndex].eventLocals.eval(null, new Locals(null, evalLocals));
|
||||
// this._eventDispatcher.dispatchEvent(elementIndex, eventName, localValues);
|
||||
this._eventDispatcher.dispatchEvent(elementIndex, eventName, evalLocals);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -35,7 +35,7 @@ export class ViewContainer {
|
||||
if (isBlank(this._lightDom)) {
|
||||
for (var i = this._views.length - 1; i >= 0; i--) {
|
||||
var view = this._views[i];
|
||||
ViewContainer.removeViewNodesFromParent(this.templateElement.parentNode, view);
|
||||
ViewContainer.removeViewNodes(view);
|
||||
this._viewFactory.returnView(view);
|
||||
}
|
||||
this._views = [];
|
||||
@ -72,12 +72,11 @@ export class ViewContainer {
|
||||
}
|
||||
|
||||
insert(view, atIndex=-1): viewModule.View {
|
||||
this._checkHydrated();
|
||||
if (atIndex == -1) atIndex = this._views.length;
|
||||
ListWrapper.insert(this._views, atIndex, view);
|
||||
if (!view.hydrated()) {
|
||||
view.hydrate(this._hostLightDom);
|
||||
}
|
||||
if (atIndex == -1) atIndex = this._views.length;
|
||||
ListWrapper.insert(this._views, atIndex, view);
|
||||
|
||||
if (isBlank(this._lightDom)) {
|
||||
ViewContainer.moveViewNodesAfterSibling(this._siblingToInsertAfter(atIndex), view);
|
||||
@ -100,7 +99,7 @@ export class ViewContainer {
|
||||
var detachedView = this.get(atIndex);
|
||||
ListWrapper.removeAt(this._views, atIndex);
|
||||
if (isBlank(this._lightDom)) {
|
||||
ViewContainer.removeViewNodesFromParent(this.templateElement.parentNode, detachedView);
|
||||
ViewContainer.removeViewNodes(detachedView);
|
||||
} else {
|
||||
this._lightDom.redistribute();
|
||||
}
|
||||
@ -129,8 +128,11 @@ export class ViewContainer {
|
||||
}
|
||||
}
|
||||
|
||||
static removeViewNodesFromParent(parent, view) {
|
||||
for (var i = view.rootNodes.length - 1; i >= 0; --i) {
|
||||
static removeViewNodes(view) {
|
||||
var len = view.rootNodes.length;
|
||||
if (len == 0) return;
|
||||
var parent = view.rootNodes[0].parentNode;
|
||||
for (var i = len - 1; i >= 0; --i) {
|
||||
DOM.removeChild(parent, view.rootNodes[i]);
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
import {OpaqueToken} from 'angular2/di';
|
||||
import {OpaqueToken, Inject, Injectable} from 'angular2/di';
|
||||
import {int, isPresent, isBlank, BaseException} from 'angular2/src/facade/lang';
|
||||
import {ListWrapper, MapWrapper, Map, StringMapWrapper, List} from 'angular2/src/facade/collection';
|
||||
|
||||
@ -8,32 +8,33 @@ import {Content} from '../shadow_dom/content_tag';
|
||||
import {ShadowDomStrategy} from '../shadow_dom/shadow_dom_strategy';
|
||||
import {EventManager} from 'angular2/src/render/dom/events/event_manager';
|
||||
|
||||
import {ViewContainer} from './view_container';
|
||||
import {ProtoView} from './proto_view';
|
||||
import {View} from './view';
|
||||
import * as vcModule from './view_container';
|
||||
import * as pvModule from './proto_view';
|
||||
import * as viewModule from './view';
|
||||
import {NG_BINDING_CLASS_SELECTOR, NG_BINDING_CLASS} from '../util';
|
||||
|
||||
export var VIEW_POOL_CAPACITY = new OpaqueToken('ViewFactory.viewPoolCapacity');
|
||||
|
||||
// TODO(tbosch): Make this an OpaqueToken as soon as our transpiler supports this!
|
||||
export const VIEW_POOL_CAPACITY = 'render.ViewFactory.viewPoolCapacity';
|
||||
|
||||
@Injectable()
|
||||
export class ViewFactory {
|
||||
_poolCapacity:number;
|
||||
_pooledViews:List<View>;
|
||||
_pooledViews:List<viewModule.View>;
|
||||
_eventManager:EventManager;
|
||||
_shadowDomStrategy:ShadowDomStrategy;
|
||||
|
||||
constructor(capacity, eventManager, shadowDomStrategy) {
|
||||
constructor(@Inject(VIEW_POOL_CAPACITY) capacity, eventManager:EventManager, shadowDomStrategy:ShadowDomStrategy) {
|
||||
this._poolCapacity = capacity;
|
||||
this._pooledViews = ListWrapper.create();
|
||||
this._eventManager = eventManager;
|
||||
this._shadowDomStrategy = shadowDomStrategy;
|
||||
}
|
||||
|
||||
getView(protoView:ProtoView):View {
|
||||
getView(protoView:pvModule.ProtoView):viewModule.View {
|
||||
// TODO(tbosch): benchmark this scanning of views and maybe
|
||||
// replace it with a fancy LRU Map/List combination...
|
||||
var view;
|
||||
for (var i=0; i<this._pooledViews.length; i++) {
|
||||
for (var i=this._pooledViews.length-1; i>=0; i--) {
|
||||
var pooledView = this._pooledViews[i];
|
||||
if (pooledView.proto === protoView) {
|
||||
view = ListWrapper.removeAt(this._pooledViews, i);
|
||||
@ -45,7 +46,7 @@ export class ViewFactory {
|
||||
return view;
|
||||
}
|
||||
|
||||
returnView(view:View) {
|
||||
returnView(view:viewModule.View) {
|
||||
if (view.hydrated()) {
|
||||
view.dehydrate();
|
||||
}
|
||||
@ -55,7 +56,7 @@ export class ViewFactory {
|
||||
}
|
||||
}
|
||||
|
||||
_createView(protoView:ProtoView): View {
|
||||
_createView(protoView:pvModule.ProtoView): viewModule.View {
|
||||
var rootElementClone = protoView.isRootView ? protoView.element : DOM.importIntoDoc(protoView.element);
|
||||
var elementsWithBindingsDynamic;
|
||||
if (protoView.isTemplateElement) {
|
||||
@ -72,7 +73,7 @@ export class ViewFactory {
|
||||
var viewRootNodes;
|
||||
if (protoView.isTemplateElement) {
|
||||
var childNode = DOM.firstChild(DOM.content(rootElementClone));
|
||||
viewRootNodes = []; // TODO(perf): Should be fixed size, since we could pre-compute in in ProtoView
|
||||
viewRootNodes = []; // TODO(perf): Should be fixed size, since we could pre-compute in in pvModule.ProtoView
|
||||
// Note: An explicit loop is the fastest way to convert a DOM array into a JS array!
|
||||
while(childNode != null) {
|
||||
ListWrapper.push(viewRootNodes, childNode);
|
||||
@ -108,7 +109,7 @@ export class ViewFactory {
|
||||
// viewContainers
|
||||
var viewContainer = null;
|
||||
if (isBlank(binder.componentId) && isPresent(binder.nestedProtoView)) {
|
||||
viewContainer = new ViewContainer(this, element);
|
||||
viewContainer = new vcModule.ViewContainer(this, element);
|
||||
}
|
||||
viewContainers[binderIdx] = viewContainer;
|
||||
|
||||
@ -120,7 +121,7 @@ export class ViewFactory {
|
||||
contentTags[binderIdx] = contentTag;
|
||||
}
|
||||
|
||||
var view = new View(
|
||||
var view = new viewModule.View(
|
||||
protoView, viewRootNodes,
|
||||
boundTextNodes, boundElements, viewContainers, contentTags
|
||||
);
|
||||
|
Reference in New Issue
Block a user