refactor(render): add `DomElement`
Replaces the multiple arrays of `DomView` by a single array with `DomElement`s. Note: this commit does not show a performance regression (tested against the tree benchmark locally).
This commit is contained in:
parent
0a50a3f564
commit
827841ec5b
|
@ -80,7 +80,7 @@ function _injectorBindings(appComponentType): List<Type | Binding | List<any>> {
|
|||
var domView = resolveInternalDomView(componentRef.hostView.render);
|
||||
// We need to do this here to ensure that we create Testability and
|
||||
// it's ready on the window for users.
|
||||
registry.registerApplication(domView.boundElements[0], testability);
|
||||
registry.registerApplication(domView.boundElements[0].element, testability);
|
||||
|
||||
return componentRef;
|
||||
});
|
||||
|
|
|
@ -23,7 +23,9 @@ export class ElementRef {
|
|||
// We need a more general way to read/write to the DOM element
|
||||
// via a proper abstraction in the render layer
|
||||
get domElement() {
|
||||
return resolveInternalDomView(this.parentView.render).boundElements[this.boundElementIndex];
|
||||
return resolveInternalDomView(this.parentView.render)
|
||||
.boundElements[this.boundElementIndex]
|
||||
.element;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -43,7 +43,9 @@ export class DebugElement {
|
|||
}
|
||||
|
||||
get domElement(): any {
|
||||
return resolveInternalDomView(this._parentView.render).boundElements[this._boundElementIndex];
|
||||
return resolveInternalDomView(this._parentView.render)
|
||||
.boundElements[this._boundElementIndex]
|
||||
.element;
|
||||
}
|
||||
|
||||
getDirectiveInstance(directiveIndex: number): any {
|
||||
|
|
|
@ -61,7 +61,7 @@ export class DebugElementViewListener implements AppViewListener {
|
|||
MapWrapper.set(_allIdsByView, view, viewId);
|
||||
var renderView = resolveInternalDomView(view.render);
|
||||
for (var i = 0; i < renderView.boundElements.length; i++) {
|
||||
_setElementId(renderView.boundElements[i], [viewId, i]);
|
||||
_setElementId(renderView.boundElements[i].element, [viewId, i]);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -16,6 +16,7 @@ import {EventManager} from './events/event_manager';
|
|||
|
||||
import {DomProtoView, DomProtoViewRef, resolveInternalDomProtoView} from './view/proto_view';
|
||||
import {DomView, DomViewRef, resolveInternalDomView} from './view/view';
|
||||
import {DomElement} from './view/element';
|
||||
import {DomViewContainer} from './view/view_container';
|
||||
import {NG_BINDING_CLASS_SELECTOR, NG_BINDING_CLASS} from './util';
|
||||
|
||||
|
@ -65,8 +66,8 @@ export class DomRenderer extends Renderer {
|
|||
componentViewRef: RenderViewRef) {
|
||||
var hostView = resolveInternalDomView(hostViewRef);
|
||||
var componentView = resolveInternalDomView(componentViewRef);
|
||||
var element = hostView.boundElements[elementIndex];
|
||||
var lightDom = hostView.lightDoms[elementIndex];
|
||||
var element = hostView.boundElements[elementIndex].element;
|
||||
var lightDom = hostView.boundElements[elementIndex].lightDom;
|
||||
if (isPresent(lightDom)) {
|
||||
lightDom.attachShadowDomView(componentView);
|
||||
}
|
||||
|
@ -92,7 +93,7 @@ export class DomRenderer extends Renderer {
|
|||
var hostView = resolveInternalDomView(hostViewRef);
|
||||
var componentView = resolveInternalDomView(componentViewRef);
|
||||
this._removeViewNodes(componentView);
|
||||
var lightDom = hostView.lightDoms[boundElementIndex];
|
||||
var lightDom = hostView.boundElements[boundElementIndex].lightDom;
|
||||
if (isPresent(lightDom)) {
|
||||
lightDom.detachShadowDomView();
|
||||
}
|
||||
|
@ -108,11 +109,11 @@ export class DomRenderer extends Renderer {
|
|||
ListWrapper.insert(viewContainer.views, atIndex, view);
|
||||
view.hostLightDom = parentView.hostLightDom;
|
||||
|
||||
var directParentLightDom = parentView.getDirectParentLightDom(boundElementIndex);
|
||||
var directParentLightDom = this._directParentLightDom(parentView, boundElementIndex);
|
||||
if (isBlank(directParentLightDom)) {
|
||||
var siblingToInsertAfter;
|
||||
if (atIndex == 0) {
|
||||
siblingToInsertAfter = parentView.boundElements[boundElementIndex];
|
||||
siblingToInsertAfter = parentView.boundElements[boundElementIndex].element;
|
||||
} else {
|
||||
siblingToInsertAfter = ListWrapper.last(viewContainer.views[atIndex - 1].rootNodes);
|
||||
}
|
||||
|
@ -130,10 +131,10 @@ export class DomRenderer extends Renderer {
|
|||
viewRef: RenderViewRef) {
|
||||
var parentView = resolveInternalDomView(parentViewRef);
|
||||
var view = resolveInternalDomView(viewRef);
|
||||
var viewContainer = parentView.viewContainers[boundElementIndex];
|
||||
var viewContainer = parentView.boundElements[boundElementIndex].viewContainer;
|
||||
var detachedView = viewContainer.views[atIndex];
|
||||
ListWrapper.removeAt(viewContainer.views, atIndex);
|
||||
var directParentLightDom = parentView.getDirectParentLightDom(boundElementIndex);
|
||||
var directParentLightDom = this._directParentLightDom(parentView, boundElementIndex);
|
||||
if (isBlank(directParentLightDom)) {
|
||||
this._removeViewNodes(detachedView);
|
||||
} else {
|
||||
|
@ -151,8 +152,8 @@ export class DomRenderer extends Renderer {
|
|||
if (view.hydrated) throw new BaseException('The view is already hydrated.');
|
||||
view.hydrated = true;
|
||||
|
||||
for (var i = 0; i < view.lightDoms.length; ++i) {
|
||||
var lightDom = view.lightDoms[i];
|
||||
for (var i = 0; i < view.boundElements.length; ++i) {
|
||||
var lightDom = view.boundElements[i].lightDom;
|
||||
if (isPresent(lightDom)) {
|
||||
lightDom.redistribute();
|
||||
}
|
||||
|
@ -244,7 +245,6 @@ export class DomRenderer extends Renderer {
|
|||
var binders = protoView.elementBinders;
|
||||
var boundTextNodes = [];
|
||||
var boundElements = ListWrapper.createFixedSize(binders.length);
|
||||
var contentTags = ListWrapper.createFixedSize(binders.length);
|
||||
|
||||
for (var binderIdx = 0; binderIdx < binders.length; binderIdx++) {
|
||||
var binder = binders[binderIdx];
|
||||
|
@ -261,7 +261,6 @@ export class DomRenderer extends Renderer {
|
|||
element = elementsWithBindings[binderIdx - protoView.rootBindingOffset];
|
||||
childNodes = DOM.childNodes(element);
|
||||
}
|
||||
boundElements[binderIdx] = element;
|
||||
|
||||
// boundTextNodes
|
||||
var textNodeIndices = binder.textNodeIndices;
|
||||
|
@ -274,10 +273,10 @@ export class DomRenderer extends Renderer {
|
|||
if (isPresent(binder.contentTagSelector)) {
|
||||
contentTag = new Content(element, binder.contentTagSelector);
|
||||
}
|
||||
contentTags[binderIdx] = contentTag;
|
||||
boundElements[binderIdx] = new DomElement(binder, element, contentTag);
|
||||
}
|
||||
|
||||
var view = new DomView(protoView, viewRootNodes, boundTextNodes, boundElements, contentTags);
|
||||
var view = new DomView(protoView, viewRootNodes, boundTextNodes, boundElements);
|
||||
|
||||
for (var binderIdx = 0; binderIdx < binders.length; binderIdx++) {
|
||||
var binder = binders[binderIdx];
|
||||
|
@ -286,21 +285,21 @@ export class DomRenderer extends Renderer {
|
|||
// lightDoms
|
||||
var lightDom = null;
|
||||
if (isPresent(binder.componentId)) {
|
||||
lightDom = this._shadowDomStrategy.constructLightDom(view, boundElements[binderIdx]);
|
||||
lightDom = this._shadowDomStrategy.constructLightDom(view, element.element);
|
||||
}
|
||||
view.lightDoms[binderIdx] = lightDom;
|
||||
element.lightDom = lightDom;
|
||||
|
||||
// init contentTags
|
||||
var contentTag = contentTags[binderIdx];
|
||||
var contentTag = element.contentTag;
|
||||
if (isPresent(contentTag)) {
|
||||
var destLightDom = view.getDirectParentLightDom(binderIdx);
|
||||
contentTag.init(destLightDom);
|
||||
var directParentLightDom = this._directParentLightDom(view, binderIdx);
|
||||
contentTag.init(directParentLightDom);
|
||||
}
|
||||
|
||||
// events
|
||||
if (isPresent(binder.eventLocals) && isPresent(binder.localEvents)) {
|
||||
for (var i = 0; i < binder.localEvents.length; i++) {
|
||||
this._createEventListener(view, element, binderIdx, binder.localEvents[i].name,
|
||||
this._createEventListener(view, element.element, binderIdx, binder.localEvents[i].name,
|
||||
binder.eventLocals);
|
||||
}
|
||||
}
|
||||
|
@ -337,14 +336,20 @@ export class DomRenderer extends Renderer {
|
|||
}
|
||||
|
||||
_getOrCreateViewContainer(parentView: DomView, boundElementIndex) {
|
||||
var vc = parentView.viewContainers[boundElementIndex];
|
||||
var el = parentView.boundElements[boundElementIndex];
|
||||
var vc = el.viewContainer;
|
||||
if (isBlank(vc)) {
|
||||
vc = new DomViewContainer();
|
||||
parentView.viewContainers[boundElementIndex] = vc;
|
||||
el.viewContainer = vc;
|
||||
}
|
||||
return vc;
|
||||
}
|
||||
|
||||
_directParentLightDom(view: DomView, boundElementIndex: number) {
|
||||
var directParentEl = view.getDirectParentElement(boundElementIndex);
|
||||
return isPresent(directParentEl) ? directParentEl.lightDom : null;
|
||||
}
|
||||
|
||||
_createGlobalEventListener(view, elementIndex, eventName, eventTarget, fullName): Function {
|
||||
return this._eventManager.addGlobalEventListener(
|
||||
eventTarget, eventName, (event) => { view.dispatchEvent(elementIndex, fullName, event); });
|
||||
|
|
|
@ -3,18 +3,13 @@ import {List, ListWrapper} from 'angular2/src/facade/collection';
|
|||
import {isBlank, isPresent} from 'angular2/src/facade/lang';
|
||||
|
||||
import * as viewModule from '../view/view';
|
||||
import * as elModule from '../view/element';
|
||||
import {Content} from './content_tag';
|
||||
|
||||
export class DestinationLightDom {}
|
||||
|
||||
class _Root {
|
||||
node;
|
||||
boundElementIndex: number;
|
||||
|
||||
constructor(node, boundElementIndex) {
|
||||
this.node = node;
|
||||
this.boundElementIndex = boundElementIndex;
|
||||
}
|
||||
constructor(public node, public boundElement: elModule.DomElement) {}
|
||||
}
|
||||
|
||||
// TODO: LightDom should implement DestinationLightDom
|
||||
|
@ -59,16 +54,14 @@ export class LightDom {
|
|||
if (view.proto.transitiveContentTagCount === 0) {
|
||||
return acc;
|
||||
}
|
||||
var contentTags = view.contentTags;
|
||||
var vcs = view.viewContainers;
|
||||
for (var i = 0; i < vcs.length; i++) {
|
||||
var vc = vcs[i];
|
||||
var contentTag = contentTags[i];
|
||||
if (isPresent(contentTag)) {
|
||||
ListWrapper.push(acc, contentTag);
|
||||
var els = view.boundElements;
|
||||
for (var i = 0; i < els.length; i++) {
|
||||
var el = els[i];
|
||||
if (isPresent(el.contentTag)) {
|
||||
ListWrapper.push(acc, el.contentTag);
|
||||
}
|
||||
if (isPresent(vc)) {
|
||||
ListWrapper.forEach(vc.contentTagContainers(),
|
||||
if (isPresent(el.viewContainer)) {
|
||||
ListWrapper.forEach(el.viewContainer.contentTagContainers(),
|
||||
(view) => { this._collectAllContentTags(view, acc); });
|
||||
}
|
||||
}
|
||||
|
@ -85,9 +78,9 @@ export class LightDom {
|
|||
var roots = this._findRoots();
|
||||
for (var i = 0; i < roots.length; ++i) {
|
||||
var root = roots[i];
|
||||
if (isPresent(root.boundElementIndex)) {
|
||||
var vc = this.lightDomView.viewContainers[root.boundElementIndex];
|
||||
var content = this.lightDomView.contentTags[root.boundElementIndex];
|
||||
if (isPresent(root.boundElement)) {
|
||||
var vc = root.boundElement.viewContainer;
|
||||
var content = root.boundElement.contentTag;
|
||||
if (isPresent(vc)) {
|
||||
res = ListWrapper.concat(res, vc.nodes());
|
||||
} else if (isPresent(content)) {
|
||||
|
@ -103,22 +96,22 @@ export class LightDom {
|
|||
}
|
||||
|
||||
// Returns a list of Roots for all the nodes of the light DOM.
|
||||
// The Root object contains the DOM node and its corresponding boundElementIndex
|
||||
// The Root object contains the DOM node and its corresponding boundElement
|
||||
private _findRoots() {
|
||||
if (isPresent(this._roots)) return this._roots;
|
||||
|
||||
var boundElements = this.lightDomView.boundElements;
|
||||
|
||||
this._roots = ListWrapper.map(this.nodes, (n) => {
|
||||
var boundElementIndex = null;
|
||||
var boundElement = null;
|
||||
for (var i = 0; i < boundElements.length; i++) {
|
||||
var boundEl = boundElements[i];
|
||||
if (isPresent(boundEl) && boundEl === n) {
|
||||
boundElementIndex = i;
|
||||
if (isPresent(boundEl) && boundEl.element === n) {
|
||||
boundElement = boundEl;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return new _Root(n, boundElementIndex);
|
||||
return new _Root(n, boundElement);
|
||||
});
|
||||
|
||||
return this._roots;
|
||||
|
|
|
@ -0,0 +1,11 @@
|
|||
import {ElementBinder} from './element_binder';
|
||||
import {DomViewContainer} from './view_container';
|
||||
import {LightDom} from '../shadow_dom/light_dom';
|
||||
import {Content} from '../shadow_dom/content_tag';
|
||||
|
||||
export class DomElement {
|
||||
viewContainer: DomViewContainer;
|
||||
lightDom: LightDom;
|
||||
constructor(public proto: ElementBinder, public element: any /* element */,
|
||||
public contentTag: Content) {}
|
||||
}
|
|
@ -3,10 +3,9 @@ import {ListWrapper, MapWrapper, Map, StringMapWrapper, List} from 'angular2/src
|
|||
import {Locals} from 'angular2/change_detection';
|
||||
import {isPresent, isBlank, BaseException} from 'angular2/src/facade/lang';
|
||||
|
||||
import {DomViewContainer} from './view_container';
|
||||
import {DomProtoView} from './proto_view';
|
||||
import {LightDom} from '../shadow_dom/light_dom';
|
||||
import {Content} from '../shadow_dom/content_tag';
|
||||
import {DomElement} from './element';
|
||||
|
||||
import {RenderViewRef, EventDispatcher} from '../../api';
|
||||
|
||||
|
@ -29,10 +28,6 @@ const NG_BINDING_CLASS = 'ng-binding';
|
|||
* Const of making objects: http://jsperf.com/instantiate-size-of-object
|
||||
*/
|
||||
export class DomView {
|
||||
// TODO(tbosch): move componentChildViews, viewContainers, contentTags, lightDoms into
|
||||
// a single array with records inside
|
||||
viewContainers: List<DomViewContainer>;
|
||||
lightDoms: List<LightDom>;
|
||||
hostLightDom: LightDom;
|
||||
shadowRoot;
|
||||
hydrated: boolean;
|
||||
|
@ -40,10 +35,7 @@ export class DomView {
|
|||
eventHandlerRemovers: List<Function>;
|
||||
|
||||
constructor(public proto: DomProtoView, public rootNodes: List</*node*/ any>,
|
||||
public boundTextNodes: List</*node*/ any>,
|
||||
public boundElements: List</*element*/ any>, public contentTags: List<Content>) {
|
||||
this.viewContainers = ListWrapper.createFixedSize(boundElements.length);
|
||||
this.lightDoms = ListWrapper.createFixedSize(boundElements.length);
|
||||
public boundTextNodes: List</*node*/ any>, public boundElements: List<DomElement>) {
|
||||
this.hostLightDom = null;
|
||||
this.hydrated = false;
|
||||
this.eventHandlerRemovers = [];
|
||||
|
@ -51,25 +43,25 @@ export class DomView {
|
|||
this.shadowRoot = null;
|
||||
}
|
||||
|
||||
getDirectParentLightDom(boundElementIndex: number) {
|
||||
getDirectParentElement(boundElementIndex: number): DomElement {
|
||||
var binder = this.proto.elementBinders[boundElementIndex];
|
||||
var destLightDom = null;
|
||||
var parent = null;
|
||||
if (binder.parentIndex !== -1 && binder.distanceToParent === 1) {
|
||||
destLightDom = this.lightDoms[binder.parentIndex];
|
||||
parent = this.boundElements[binder.parentIndex];
|
||||
}
|
||||
return destLightDom;
|
||||
return parent;
|
||||
}
|
||||
|
||||
setElementProperty(elementIndex: number, propertyName: string, value: any) {
|
||||
var setter =
|
||||
MapWrapper.get(this.proto.elementBinders[elementIndex].propertySetters, propertyName);
|
||||
setter(this.boundElements[elementIndex], value);
|
||||
setter(this.boundElements[elementIndex].element, value);
|
||||
}
|
||||
|
||||
callAction(elementIndex: number, actionExpression: string, actionArgs: any) {
|
||||
var binder = this.proto.elementBinders[elementIndex];
|
||||
var hostAction = MapWrapper.get(binder.hostActions, actionExpression);
|
||||
hostAction.eval(this.boundElements[elementIndex], this._localsWithAction(actionArgs));
|
||||
hostAction.eval(this.boundElements[elementIndex].element, this._localsWithAction(actionArgs));
|
||||
}
|
||||
|
||||
_localsWithAction(action: Object): Locals {
|
||||
|
|
|
@ -123,7 +123,7 @@ export class DomTestbed {
|
|||
}
|
||||
|
||||
triggerEvent(viewRef: RenderViewRef, boundElementIndex: number, eventName: string) {
|
||||
var element = resolveInternalDomView(viewRef).boundElements[boundElementIndex];
|
||||
var element = resolveInternalDomView(viewRef).boundElements[boundElementIndex].element;
|
||||
dispatchEvent(element, eventName);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -18,6 +18,7 @@ import {LightDom} from 'angular2/src/render/dom/shadow_dom/light_dom';
|
|||
import {DomView} from 'angular2/src/render/dom/view/view';
|
||||
import {DomProtoView} from 'angular2/src/render/dom/view/proto_view';
|
||||
import {DomViewContainer} from 'angular2/src/render/dom/view/view_container';
|
||||
import {DomElement} from 'angular2/src/render/dom/view/element';
|
||||
|
||||
@proxy
|
||||
@IMPLEMENTS(DomProtoView)
|
||||
|
@ -31,31 +32,27 @@ class FakeProtoView extends SpyObject {
|
|||
@IMPLEMENTS(DomView)
|
||||
class FakeView extends SpyObject {
|
||||
boundElements;
|
||||
contentTags;
|
||||
viewContainers;
|
||||
proto;
|
||||
|
||||
constructor(containers = null, transitiveContentTagCount: number = 1) {
|
||||
super(DomView);
|
||||
this.proto = new FakeProtoView(transitiveContentTagCount);
|
||||
this.boundElements = [];
|
||||
this.contentTags = [];
|
||||
this.viewContainers = [];
|
||||
if (isPresent(containers)) {
|
||||
ListWrapper.forEach(containers, (c) => {
|
||||
var boundElement = null;
|
||||
var element = null;
|
||||
var contentTag = null;
|
||||
var vc = null;
|
||||
if (c instanceof FakeContentTag) {
|
||||
contentTag = c;
|
||||
boundElement = c.contentStartElement;
|
||||
element = c.contentStartElement;
|
||||
}
|
||||
if (c instanceof FakeViewContainer) {
|
||||
vc = c;
|
||||
boundElement = c.templateElement;
|
||||
element = c.templateElement;
|
||||
}
|
||||
ListWrapper.push(this.contentTags, contentTag);
|
||||
ListWrapper.push(this.viewContainers, vc);
|
||||
var boundElement = new DomElement(null, element, contentTag);
|
||||
boundElement.viewContainer = vc;
|
||||
ListWrapper.push(this.boundElements, boundElement);
|
||||
});
|
||||
}
|
||||
|
|
|
@ -15,13 +15,13 @@ import {
|
|||
SpyObject,
|
||||
proxy
|
||||
} from 'angular2/test_lib';
|
||||
import {IMPLEMENTS, isBlank} from 'angular2/src/facade/lang';
|
||||
import {isBlank} from 'angular2/src/facade/lang';
|
||||
import {ListWrapper} from 'angular2/src/facade/collection';
|
||||
|
||||
import {DomProtoView} from 'angular2/src/render/dom/view/proto_view';
|
||||
import {ElementBinder} from 'angular2/src/render/dom/view/element_binder';
|
||||
import {DomView} from 'angular2/src/render/dom/view/view';
|
||||
import {LightDom} from 'angular2/src/render/dom/shadow_dom/light_dom';
|
||||
import {DomElement} from 'angular2/src/render/dom/view/element';
|
||||
import {DOM} from 'angular2/src/dom/dom_adapter';
|
||||
|
||||
export function main() {
|
||||
|
@ -42,20 +42,19 @@ export function main() {
|
|||
var root = el('<div><div></div></div>');
|
||||
var boundElements = [];
|
||||
for (var i = 0; i < boundElementCount; i++) {
|
||||
ListWrapper.push(boundElements, el('<span></span'));
|
||||
ListWrapper.push(boundElements,
|
||||
new DomElement(pv.elementBinders[i], el('<span></span'), null));
|
||||
}
|
||||
return new DomView(pv, [DOM.childNodes(root)[0]], [], boundElements, []);
|
||||
return new DomView(pv, [DOM.childNodes(root)[0]], [], boundElements);
|
||||
}
|
||||
|
||||
describe('getDirectParentLightDom', () => {
|
||||
describe('getDirectParentElement', () => {
|
||||
|
||||
it('should return the LightDom of the direct parent', () => {
|
||||
it('should return the DomElement of the direct parent', () => {
|
||||
var pv = createProtoView(
|
||||
[new ElementBinder(), new ElementBinder({parentIndex: 0, distanceToParent: 1})]);
|
||||
var view = createView(pv, 2);
|
||||
view.lightDoms[0] = <any>new SpyLightDom();
|
||||
view.lightDoms[1] = <any>new SpyLightDom();
|
||||
expect(view.getDirectParentLightDom(1)).toBe(view.lightDoms[0]);
|
||||
expect(view.getDirectParentElement(1)).toBe(view.boundElements[0]);
|
||||
});
|
||||
|
||||
it('should return null if the direct parent is not bound', () => {
|
||||
|
@ -65,20 +64,10 @@ export function main() {
|
|||
new ElementBinder({parentIndex: 0, distanceToParent: 2})
|
||||
]);
|
||||
var view = createView(pv, 3);
|
||||
view.lightDoms[0] = <any>new SpyLightDom();
|
||||
view.lightDoms[1] = <any>new SpyLightDom();
|
||||
view.lightDoms[2] = <any>new SpyLightDom();
|
||||
expect(view.getDirectParentLightDom(2)).toBe(null);
|
||||
expect(view.getDirectParentElement(2)).toBe(null);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
@proxy
|
||||
@IMPLEMENTS(LightDom)
|
||||
class SpyLightDom extends SpyObject {
|
||||
constructor() { super(LightDom); }
|
||||
noSuchMethod(m) { return super.noSuchMethod(m) }
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue