design: view instantiation test
This commit is contained in:
parent
2d19e7122b
commit
a9896ed391
|
@ -2,7 +2,7 @@ import {FIELD} from 'facade/lang';
|
|||
|
||||
/**
|
||||
|
||||
Difference beteween di.Injector and ElementInjector
|
||||
Difference between di.Injector and ElementInjector
|
||||
|
||||
di.Injector (di.Module):
|
||||
- imperative based (can create child injectors imperativly)
|
||||
|
@ -16,14 +16,47 @@ ElementInjector (ElementModule):
|
|||
- Fast
|
||||
- Query mechanism for children
|
||||
- 1:1 to DOM structure.
|
||||
|
||||
PERF BENCHMARK: http://www.williambrownstreet.net/blog/2014/04/faster-angularjs-rendering-angularjs-and-reactjs/
|
||||
|
||||
*/
|
||||
|
||||
export class ProtoElementInjector {
|
||||
@FIELD('final _parent:ProtoElementInjector')
|
||||
/// Temporory instance while instantiating
|
||||
@FIELD('_clone:ElementInjector')
|
||||
/**
|
||||
parent:ProtoDirectiveInjector;
|
||||
next:ProtoDirectiveInjector;
|
||||
prev:ProtoDirectiveInjector;
|
||||
head:ProtoDirectiveInjector;
|
||||
tail:ProtoDirectiveInjector;
|
||||
DirectiveInjector cloneingInstance;
|
||||
KeyMap keyMap;
|
||||
/// Because DI tree is sparse, this shows how far away is the Parent DI
|
||||
parentDistance:int = 1; /// 1 for non-sparse/normal depth.
|
||||
|
||||
cKey:int; cFactory:Function; cParams:List<int>;
|
||||
keyId0:int; factory0:Function; params0:List<int>;
|
||||
keyId1:int; factory1:Function; params1:List<int>;
|
||||
keyId2:int; factory2:Function; params2:List<int>;
|
||||
keyId3:int; factory3:Function; params3:List<int>;
|
||||
keyId4:int; factory4:Function; params4:List<int>;
|
||||
keyId5:int; factory5:Function; params5:List<int>;
|
||||
keyId6:int; factory6:Function; params6:List<int>;
|
||||
keyId7:int; factory7:Function; params7:List<int>;
|
||||
keyId8:int; factory8:Function; params8:List<int>;
|
||||
keyId9:int; factory9:Function; params9:List<int>;
|
||||
|
||||
queryKeyId0:int;
|
||||
queryKeyId1:int;
|
||||
|
||||
textNodes:List<int>;
|
||||
hasProperties:bool;
|
||||
events:Map<string, Expression>;
|
||||
|
||||
elementInjector:ElementInjector;
|
||||
*/
|
||||
constructor(parent:ProtoElementInjector) {
|
||||
this._parent = parent;
|
||||
this.hasProperties = false;
|
||||
this.textNodes = null;
|
||||
}
|
||||
|
||||
instantiate():ElementInjector {
|
||||
|
@ -32,6 +65,48 @@ export class ProtoElementInjector {
|
|||
}
|
||||
|
||||
export class ElementInjector {
|
||||
/*
|
||||
_protoInjector:ProtoElementInjector;
|
||||
injector:Injector;
|
||||
_parent:ElementInjector;
|
||||
_next:ElementInjector;
|
||||
_prev:ElementInjector;
|
||||
_head:ElementInjector;
|
||||
_tail:ElementInjector;
|
||||
|
||||
|
||||
// For performance reasons the Injector only supports 10 directives per element.
|
||||
// NOTE: linear search over fields is faster than HashMap lookup.
|
||||
_cObj; // Component only
|
||||
_obj0;
|
||||
_obj1;
|
||||
_obj2;
|
||||
_obj3;
|
||||
_obj4;
|
||||
_obj5;
|
||||
_obj6;
|
||||
_obj7;
|
||||
_obj8;
|
||||
_obj9;
|
||||
|
||||
element:Element;
|
||||
ngElement:NgElement;
|
||||
shadowRoot:ShadowRoot;
|
||||
elementProbe:ElementProbe;
|
||||
view:View;
|
||||
viewPort:ViewPort;
|
||||
viewFactory:ViewFactory;
|
||||
animate:Animate;
|
||||
destinationLightDom:DestinationLightDom;
|
||||
sourceLightDom:SourceLightDom;
|
||||
|
||||
|
||||
// For performance reasons the Injector only supports 2 [Query] per element.
|
||||
// NOTE: linear search over fields is faster than HashMap lookup.
|
||||
_query0:Query;
|
||||
_query1:Query;
|
||||
|
||||
*/
|
||||
@FIELD('final protoInjector:ProtoElementInjector')
|
||||
constructor(protoInjector:ProtoElementInjector) {
|
||||
this.protoInjector = protoInjector;
|
||||
|
|
|
@ -13,7 +13,7 @@ export class Selector {
|
|||
* @param elementName Name of the element
|
||||
* @param attributes Attributes on the Element.
|
||||
*/
|
||||
visitElement(elementName:String, attributes:Map<string, string>):List<AnnotatedType> {
|
||||
visitElement(elementName:string, attributes:Map<string, string>):List<AnnotatedType> {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import {DOM, Node, DocumentFragment, TemplateElement} from 'facade/dom';
|
||||
import {DOM, Element, Node, Text, DocumentFragment, TemplateElement} from 'facade/dom';
|
||||
import {ListWrapper} from 'facade/collection';
|
||||
import {ProtoWatchGroup, WatchGroup, WatchGroupDispatcher} from 'change_detection/watch_group';
|
||||
import {Record} from 'change_detection/record';
|
||||
|
@ -8,56 +8,66 @@ import {SetterFn} from 'change_detection/facade';
|
|||
import {FIELD, IMPLEMENTS} from 'facade/lang';
|
||||
import {List} from 'facade/collection';
|
||||
|
||||
/***
|
||||
* Const of making objects: http://jsperf.com/instantiate-size-of-object
|
||||
*/
|
||||
@IMPLEMENTS(WatchGroupDispatcher)
|
||||
export class View {
|
||||
@FIELD('final _fragment:DocumentFragment')
|
||||
@FIELD('final fragment:DocumentFragment')
|
||||
/// This list matches the _nodes list. It is sparse, since only Elements have ElementInjector
|
||||
@FIELD('final _rootElementInjectors:List<ElementInjector>')
|
||||
@FIELD('final _elementInjectors:List<ElementInjector>')
|
||||
@FIELD('final _textNodes:List<Text>')
|
||||
@FIELD('final _watchGroup:WatchGroup')
|
||||
@FIELD('final rootElementInjectors:List<ElementInjector>')
|
||||
@FIELD('final elementInjectors:List<ElementInjector>')
|
||||
@FIELD('final bindElements:List<Element>')
|
||||
@FIELD('final textNodes:List<Text>')
|
||||
@FIELD('final watchGroup:WatchGroup')
|
||||
/// When the view is part of render tree, the DocumentFragment is empty, which is why we need
|
||||
/// to keep track of the nodes.
|
||||
@FIELD('final _nodes:List<Node>')
|
||||
@FIELD('final _onChangeDispatcher:OnChangeDispatcher')
|
||||
@FIELD('final nodes:List<Node>')
|
||||
@FIELD('final onChangeDispatcher:OnChangeDispatcher')
|
||||
constructor(fragment:DocumentFragment) {
|
||||
this._fragment = fragment;
|
||||
this._nodes = ListWrapper.clone(fragment.childNodes);
|
||||
this._onChangeDispatcher = null;
|
||||
this._elementInjectors = null;
|
||||
this._textNodes = null;
|
||||
this.fragment = fragment;
|
||||
this.nodes = ListWrapper.clone(fragment.childNodes);
|
||||
this.onChangeDispatcher = null;
|
||||
this.elementInjectors = null;
|
||||
this.textNodes = null;
|
||||
this.bindElements = null;
|
||||
}
|
||||
|
||||
onRecordChange(record:Record, target) {
|
||||
// dispatch to element injector or text nodes based on context
|
||||
if (target instanceof ElementInjectorTarget) {
|
||||
// we know that it is ElementInjectorTarget
|
||||
var eTarget:ElementInjectorTarget = target;
|
||||
this._onChangeDispatcher.notify(this, eTarget);
|
||||
eTarget.invoke(record, this._elementInjectors);
|
||||
if (target instanceof DirectivePropertyMemento) {
|
||||
// we know that it is DirectivePropertyMemento
|
||||
var directiveMemento:DirectivePropertyMemento = target;
|
||||
directiveMemento.invoke(record, this.elementInjectors);
|
||||
} else if (target instanceof ElementPropertyMemento) {
|
||||
var elementMemento:ElementPropertyMemento = target;
|
||||
elementMemento.invoke(record, this.bindElements);
|
||||
} else {
|
||||
// we know it refferst to _textNodes.
|
||||
// we know it refers to _textNodes.
|
||||
var textNodeIndex:number = target;
|
||||
DOM.setText(this._textNodes[textNodeIndex], record.currentValue);
|
||||
DOM.setText(this.textNodes[textNodeIndex], record.currentValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export class ProtoView {
|
||||
@FIELD('final _template:TemplateElement')
|
||||
@FIELD('final _module:Module')
|
||||
@FIELD('final _protoElementInjectors:List<ProtoElementInjector>')
|
||||
@FIELD('final _protoWatchGroup:ProtoWatchGroup')
|
||||
@FIELD('final _template:TemplateElement')
|
||||
@FIELD('final _module:Module')
|
||||
@FIELD('final _protoElementInjectors:List<ProtoElementInjector>')
|
||||
@FIELD('final _protoWatchGroup:ProtoWatchGroup')
|
||||
@FIELD('final _useRootElement:bool')
|
||||
constructor(
|
||||
template:TemplateElement,
|
||||
module:Module,
|
||||
protoElementInjector:ProtoElementInjector,
|
||||
protoWatchGroup:ProtoWatchGroup)
|
||||
protoElementInjector:List<ProtoElementInjector>,
|
||||
protoWatchGroup:ProtoWatchGroup,
|
||||
useRootElement:bool)
|
||||
{
|
||||
this._template = template;
|
||||
this._module = module;
|
||||
this._protoElementInjectors = protoElementInjector;
|
||||
this._protoWatchGroup = protoWatchGroup;
|
||||
this._useRootElement = useRootElement;
|
||||
}
|
||||
|
||||
instantiate():View {
|
||||
|
@ -65,8 +75,21 @@ export class ProtoView {
|
|||
}
|
||||
}
|
||||
|
||||
export class ElementPropertyMemento {
|
||||
@FIELD('final _elementIndex:int')
|
||||
@FIELD('final _propertyIndex:string')
|
||||
constructor(elementIndex:int, propertyName:string) {
|
||||
this._elementIndex = elementIndex;
|
||||
this._propertyName = propertyName;
|
||||
}
|
||||
|
||||
export class ElementInjectorTarget {
|
||||
invoke(record:Record, elementInjectors:List<Element>) {
|
||||
var element:Element = elementInjectors[this._elementIndex];
|
||||
DOM.setProperty(element, this._propertyName, record.currentValue);
|
||||
}
|
||||
}
|
||||
|
||||
export class DirectivePropertyMemento {
|
||||
@FIELD('final _elementInjectorIndex:int')
|
||||
@FIELD('final _directiveIndex:int')
|
||||
@FIELD('final _setterName:String')
|
||||
|
@ -97,13 +120,13 @@ export class ElementInjectorTarget {
|
|||
export class OnChangeDispatcher {
|
||||
|
||||
@FIELD('_lastView:View')
|
||||
@FIELD('_lastTarget:ElementInjectorTarget')
|
||||
@FIELD('_lastTarget:DirectivePropertyMemento')
|
||||
constructor() {
|
||||
this._lastView = null;
|
||||
this._lastTarget = null;
|
||||
}
|
||||
|
||||
notify(view:View, eTarget:ElementInjectorTarget) {
|
||||
notify(view:View, eTarget:DirectivePropertyMemento) {
|
||||
|
||||
}
|
||||
|
||||
|
|
|
@ -1,16 +1,50 @@
|
|||
import {describe, it, expect} from 'test_lib/test_lib';
|
||||
import {describe, xit, it, expect} from 'test_lib/test_lib';
|
||||
import {ProtoWatchGroup} from 'change_detection/watch_group';
|
||||
import {ProtoView, View} from 'core/compiler/view';
|
||||
import {DOM} from 'facade/dom';
|
||||
import {ProtoElementInjector, ElementInjector} from 'core/compiler/element_injector';
|
||||
import {DOM, Element} from 'facade/dom';
|
||||
import {Module} from 'di/di';
|
||||
|
||||
export function main() {
|
||||
describe('view', function() {
|
||||
describe('ProtoView', function() {
|
||||
it('should create an instance of view', function() {
|
||||
var template = DOM.createTemplate('Hello <b>world</b>!');
|
||||
var pv = new ProtoView(template, null, null, null);
|
||||
var pv = new ProtoView(template, null, null, null, false);
|
||||
var view:View = pv.instantiate();
|
||||
expect(view instanceof View).toBe(true);
|
||||
});
|
||||
|
||||
|
||||
xit('should create view instance and locate basic parts', function() {
|
||||
var template = DOM.createTemplate(
|
||||
'<section class="ng-binding" no-injector>' +
|
||||
'Hello {}!' +
|
||||
'<div directive class="ng-binding" injector>' +
|
||||
'<span class="ng-binding" [hidden]="exp" no-injector>don\'t show me</span>' +
|
||||
'</div>' +
|
||||
'</section>');
|
||||
var module:Module = null;
|
||||
var sectionPI = new ProtoElementInjector(null);
|
||||
sectionPI.textNodes = [0];
|
||||
var divPI = new ProtoElementInjector(null);
|
||||
var spanPI = new ProtoElementInjector(null);
|
||||
spanPI.hasProperties = true;
|
||||
var protoElementInjector:List<ProtoElementInjector> = [sectionPI, divPI, spanPI];
|
||||
var protoWatchGroup:ProtoWatchGroup = null;
|
||||
var hasSingleRoot:bool = false;
|
||||
var pv = new ProtoView(template, module, protoElementInjector, protoWatchGroup, hasSingleRoot);
|
||||
var view:View = pv.instantiate();
|
||||
var section:Element = template.content.firstChild;
|
||||
var div:Element = DOM.getElementsByTagName(section, 'div');
|
||||
var span:Element = DOM.getElementsByTagName(div, 'span');
|
||||
expect(DOM.getInnerHTML(view.fragment)).toEqual(DOM.getInnerHTML(section)); // exclude top level <section>
|
||||
expect(view.nodes).toEqual([view.fragment.firstChild.childNodes]); // TextNode(Hello...), <div>
|
||||
var elementInjector:ElementInjector = view.elementInjectors[1];
|
||||
expect(view.elementInjectors).toEqual([null, elementInjector, null]); // only second one has directive
|
||||
expect(view.bindElements).toEqual([span]);
|
||||
expect(view.textNodes).toEqual([section.childNodes[0]]);
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
library angular.core.facade.dom;
|
||||
|
||||
import 'dart:html';
|
||||
import 'dart:js' show JsObject;
|
||||
|
||||
export 'dart:html' show DocumentFragment, Node, Element, TemplateElement, Text;
|
||||
|
||||
|
@ -28,4 +29,13 @@ class DOM {
|
|||
static clone(Node node) {
|
||||
return node.clone(true);
|
||||
}
|
||||
static setProperty(Element element, String name, value) {
|
||||
new JsObject.fromBrowserObject(element)[name] = value;
|
||||
}
|
||||
static getElementsByClassName(Element element, String name) {
|
||||
return element.getElementsByClassName(name);
|
||||
}
|
||||
static getElementsByTagName(Element element, String name) {
|
||||
return element.querySelectorAll(name);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -28,4 +28,13 @@ export class DOM {
|
|||
static clone(node:Node) {
|
||||
return node.cloneNode(true);
|
||||
}
|
||||
static setProperty(element:Element, name:string, value) {
|
||||
element[name] = value;
|
||||
}
|
||||
static getElementsByClassName(element:Element, name:string) {
|
||||
return element.getElementsByClassName(name);
|
||||
}
|
||||
static getElementsByTagName(element:Element, name:string) {
|
||||
return element.getElementsByTagName(name);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -57,3 +57,4 @@ class NumberWrapper {
|
|||
return double.parse(text);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -11,7 +11,6 @@ export class Parser extends TraceurParser {
|
|||
parseTypeName_() {
|
||||
// Copy of original implementation
|
||||
var typeName = super.parseTypeName_();
|
||||
var next = this.peekType_();
|
||||
// Generics support
|
||||
if (this.eatIf_(OPEN_ANGLE)) {
|
||||
var generics = [];
|
||||
|
@ -34,4 +33,4 @@ export class Parser extends TraceurParser {
|
|||
} while (this.eatIf_(COMMA));
|
||||
this.eat_(CLOSE_CURLY);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue