Skip to content

Commit b0c9d05

Browse files
committed
feat(view): add onChange implementation to view.
1 parent e1c84e0 commit b0c9d05

File tree

7 files changed

+162
-52
lines changed

7 files changed

+162
-52
lines changed

modules/benchmarks/src/element_injector/instantiate_benchmark.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ export function run () {
88
var appInjector = new Injector([]);
99

1010
var bindings = [A, B, C];
11-
var proto = new ProtoElementInjector(null, bindings, []);
11+
var proto = new ProtoElementInjector(null, bindings, [], false);
1212
for (var i = 0; i < ITERATIONS; ++i) {
1313
var ei = proto.instantiate({view:null});
1414
ei.instantiateDirectives(appInjector);
@@ -31,4 +31,4 @@ class C {
3131
constructor(a:A, b:B) {
3232
count++;
3333
}
34-
}
34+
}

modules/benchmarks/src/element_injector/instantiate_benchmark_codegen.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ export function run () {
1616
], false)];
1717

1818

19-
var proto = new ProtoElementInjector(null, bindings, []);
19+
var proto = new ProtoElementInjector(null, bindings, [], false);
2020
for (var i = 0; i < ITERATIONS; ++i) {
2121
var ei = proto.instantiate({view:null});
2222
ei.instantiateDirectives(appInjector);

modules/benchmarks/src/element_injector/instantiate_directive_benchmark.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ export function run () {
88
var appInjector = new Injector([]);
99

1010
var bindings = [A, B, C];
11-
var proto = new ProtoElementInjector(null, bindings, []);
11+
var proto = new ProtoElementInjector(null, bindings, [], false);
1212
var ei = proto.instantiate({view:null});
1313

1414
for (var i = 0; i < ITERATIONS; ++i) {

modules/core/src/compiler/element_injector.js

Lines changed: 29 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -118,7 +118,6 @@ export class ProtoElementInjector extends TreeNode {
118118
query_keyId1:int;
119119
120120
textNodes:List<int>;
121-
hasProperties:boolean;
122121
events:Map<string, Expression>;
123122
124123
elementInjector:ElementInjector;
@@ -144,8 +143,10 @@ export class ProtoElementInjector extends TreeNode {
144143
@FIELD('_key7:int')
145144
@FIELD('_key8:int')
146145
@FIELD('_key9:int')
147-
@FIELD('textNodes:List<int>')
148-
constructor(parent:ProtoElementInjector, bindings:List, textNodes:List) {
146+
@FIELD('textNodeIndices:List<int>')
147+
@FIELD('hasElementPropertyBindings:bool')
148+
constructor(parent:ProtoElementInjector, bindings:List, textNodeIndices:List,
149+
hasElementPropertyBindings:boolean) {
149150
super(parent);
150151

151152
this._elementInjector = null;
@@ -177,10 +178,8 @@ export class ProtoElementInjector extends TreeNode {
177178
throw 'Maximum number of directives per element has been reached.';
178179
}
179180

180-
this.textNodes = textNodes;
181-
182-
// dummy fields to make analyzer happy
183-
this.hasProperties = false;
181+
this.textNodeIndices = textNodeIndices;
182+
this.hasElementPropertyBindings = hasElementPropertyBindings;
184183
}
185184

186185
instantiate({view}):ElementInjector {
@@ -418,5 +417,28 @@ export class ElementInjector extends TreeNode {
418417
if (p._keyId9 === keyId) return this._obj9;
419418
return _undefined;
420419
}
420+
421+
getAtIndex(index:int) {
422+
if (index == 0) return this._obj0;
423+
if (index == 1) return this._obj1;
424+
if (index == 2) return this._obj2;
425+
if (index == 3) return this._obj3;
426+
if (index == 4) return this._obj4;
427+
if (index == 5) return this._obj5;
428+
if (index == 6) return this._obj6;
429+
if (index == 7) return this._obj7;
430+
if (index == 8) return this._obj8;
431+
if (index == 9) return this._obj9;
432+
throw new OutOfBoundsAccess(index);
433+
}
421434
}
422435

436+
class OutOfBoundsAccess extends Error {
437+
constructor(index) {
438+
this.message = `Index ${index} is out-of-bounds.`;
439+
}
440+
441+
toString() {
442+
return this.message;
443+
}
444+
}

modules/core/src/compiler/view.js

Lines changed: 33 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -23,14 +23,15 @@ export class View {
2323
/// to keep track of the nodes.
2424
@FIELD('final nodes:List<Node>')
2525
@FIELD('final onChangeDispatcher:OnChangeDispatcher')
26-
constructor(fragment:DocumentFragment, elementInjector:List, rootElementInjectors:List, textNodes:List) {
26+
constructor(fragment:DocumentFragment, elementInjector:List,
27+
rootElementInjectors:List, textNodes:List, bindElements:List) {
2728
this.fragment = fragment;
2829
this.nodes = ListWrapper.clone(fragment.childNodes);
2930
this.elementInjectors = elementInjector;
3031
this.rootElementInjectors = rootElementInjectors;
3132
this.onChangeDispatcher = null;
3233
this.textNodes = textNodes;
33-
this.bindElements = null;
34+
this.bindElements = bindElements;
3435
}
3536

3637
onRecordChange(record:Record, target) {
@@ -39,7 +40,7 @@ export class View {
3940
// we know that it is DirectivePropertyMemento
4041
var directiveMemento:DirectivePropertyMemento = target;
4142
directiveMemento.invoke(record, this.elementInjectors);
42-
} else if (target instanceof ElementPropertyMemento) {
43+
} else if (target instanceof ElementPropertyMemento) {
4344
var elementMemento:ElementPropertyMemento = target;
4445
elementMemento.invoke(record, this.bindElements);
4546
} else {
@@ -83,16 +84,22 @@ export class ProtoView {
8384
var elementInjectors = ProtoView._createElementInjectors(elements, protos);
8485
var rootElementInjectors = ProtoView._rootElementInjectors(elementInjectors);
8586
var textNodes = ProtoView._textNodes(elements, protos);
87+
var bindElements = ProtoView._bindElements(elements, protos);
8688

87-
return new View(fragment, elementInjectors, rootElementInjectors, textNodes);
89+
return new View(fragment, elementInjectors, rootElementInjectors, textNodes,
90+
bindElements);
8891
}
8992

9093
static _createElementInjectors(elements, protos) {
9194
var injectors = ListWrapper.createFixedSize(protos.length);
9295
for (var i = 0; i < protos.length; ++i) {
9396
injectors[i] = ProtoView._createElementInjector(elements[i], protos[i]);
9497
}
95-
ListWrapper.forEach(protos, p => p.clearElementInjector());
98+
// Cannot be rolled into loop above, because parentInjector pointers need
99+
// to be set on the children.
100+
for (var i = 0; i < protos.length; ++i) {
101+
protos[i].clearElementInjector();
102+
}
96103
return injectors;
97104
}
98105

@@ -108,16 +115,26 @@ export class ProtoView {
108115
static _textNodes(elements, protos) {
109116
var textNodes = [];
110117
for (var i = 0; i < protos.length; ++i) {
111-
ProtoView._collectTextNodes(textNodes, elements[i], protos[i]);
118+
ProtoView._collectTextNodes(textNodes, elements[i],
119+
protos[i].textNodeIndices);
112120
}
113121
return textNodes;
114122
}
115123

116-
static _collectTextNodes(allTextNodes, element, proto) {
124+
static _bindElements(elements, protos):List<Element> {
125+
var bindElements = [];
126+
for (var i = 0; i < protos.length; ++i) {
127+
if (protos[i].hasElementPropertyBindings) ListWrapper.push(
128+
bindElements, elements[i]);
129+
}
130+
return bindElements;
131+
}
132+
133+
static _collectTextNodes(allTextNodes, element, indices) {
117134
var childNodes = DOM.childNodes(element);
118-
ListWrapper.forEach(proto.textNodes, (i) => {
119-
ListWrapper.push(allTextNodes, childNodes[i]);
120-
});
135+
for (var i = 0; i < indices.length; ++i) {
136+
ListWrapper.push(allTextNodes, childNodes[indices[i]]);
137+
}
121138
}
122139
}
123140

@@ -129,23 +146,22 @@ export class ElementPropertyMemento {
129146
this._propertyName = propertyName;
130147
}
131148

132-
invoke(record:Record, elementInjectors:List<Element>) {
133-
var element:Element = elementInjectors[this._elementIndex];
149+
invoke(record:Record, bindElements:List<Element>) {
150+
var element:Element = bindElements[this._elementIndex];
134151
DOM.setProperty(element, this._propertyName, record.currentValue);
135152
}
136153
}
137154

138155
export class DirectivePropertyMemento {
139156
@FIELD('final _elementInjectorIndex:int')
140157
@FIELD('final _directiveIndex:int')
141-
@FIELD('final _setterName:String')
158+
@FIELD('final _setterName:string')
142159
@FIELD('final _setter:SetterFn')
143160
constructor(
144161
elementInjectorIndex:number,
145162
directiveIndex:number,
146-
setterName:String,
147-
setter:SetterFn)
148-
{
163+
setterName:string,
164+
setter:SetterFn) {
149165
this._elementInjectorIndex = elementInjectorIndex;
150166
this._directiveIndex = directiveIndex;
151167
this._setterName = setterName;
@@ -154,7 +170,7 @@ export class DirectivePropertyMemento {
154170

155171
invoke(record:Record, elementInjectors:List<ElementInjector>) {
156172
var elementInjector:ElementInjector = elementInjectors[this._elementInjectorIndex];
157-
var directive = elementInjectors[this._directiveIndex];
173+
var directive = elementInjector.getAtIndex(this._directiveIndex);
158174
this._setter(directive, record.currentValue);
159175
}
160176
}

modules/core/test/compiler/element_injector_spec.js

Lines changed: 22 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ export function main() {
6262
if (isBlank(appInjector)) appInjector = new Injector([]);
6363
if (isBlank(props)) props = {};
6464

65-
var proto = new ProtoElementInjector(null, bindings, []);
65+
var proto = new ProtoElementInjector(null, bindings, [], false);
6666
var inj = proto.instantiate({view: props["view"]});
6767
inj.instantiateDirectives(appInjector);
6868
return inj;
@@ -71,11 +71,12 @@ export function main() {
7171
function parentChildInjectors(parentBindings, childBindings) {
7272
var inj = new Injector([]);
7373

74-
var protoParent = new ProtoElementInjector(null, parentBindings, []);
74+
var protoParent = new ProtoElementInjector(null, parentBindings, [], false);
7575
var parent = protoParent.instantiate({view: null});
7676
parent.instantiateDirectives(inj);
7777

78-
var protoChild = new ProtoElementInjector(protoParent, childBindings, []);
78+
var protoChild = new ProtoElementInjector(protoParent, childBindings, [],
79+
false);
7980
var child = protoChild.instantiate({view: null});
8081
child.instantiateDirectives(inj);
8182

@@ -85,9 +86,9 @@ export function main() {
8586
describe("ElementInjector", function () {
8687
describe("proto injectors", function () {
8788
it("should construct a proto tree", function () {
88-
var p = new ProtoElementInjector(null, [], []);
89-
var c1 = new ProtoElementInjector(p, [], []);
90-
var c2 = new ProtoElementInjector(p, [], []);
89+
var p = new ProtoElementInjector(null, [], [], false);
90+
var c1 = new ProtoElementInjector(p, [], [], false);
91+
var c2 = new ProtoElementInjector(p, [], [], false);
9192

9293
expect(humanize(p, [
9394
[p, 'parent'],
@@ -99,9 +100,9 @@ export function main() {
99100

100101
describe("instantiate", function () {
101102
it("should create an element injector", function () {
102-
var protoParent = new ProtoElementInjector(null, [], []);
103-
var protoChild1 = new ProtoElementInjector(protoParent, [], []);
104-
var protoChild2 = new ProtoElementInjector(protoParent, [], []);
103+
var protoParent = new ProtoElementInjector(null, [], [], false);
104+
var protoChild1 = new ProtoElementInjector(protoParent, [], [], false);
105+
var protoChild2 = new ProtoElementInjector(protoParent, [], [], false);
105106

106107
var p = protoParent.instantiate({view: null});
107108
var c1 = protoChild1.instantiate({view: null});
@@ -117,12 +118,12 @@ export function main() {
117118

118119
describe("hasBindings", function () {
119120
it("should be true when there are bindings", function () {
120-
var p = new ProtoElementInjector(null, [Directive], []);
121+
var p = new ProtoElementInjector(null, [Directive], [], false);
121122
expect(p.hasBindings).toBeTruthy();
122123
});
123124

124125
it("should be false otherwise", function () {
125-
var p = new ProtoElementInjector(null, [], []);
126+
var p = new ProtoElementInjector(null, [], [], false);
126127
expect(p.hasBindings).toBeFalsy();
127128
});
128129
});
@@ -196,6 +197,15 @@ export function main() {
196197
var inj = injector([bind(Directive).toClass(Directive)]);
197198
expect(inj.get(Directive)).toBeAnInstanceOf(Directive);
198199
});
200+
201+
it("should allow for direct access using getAtIndex", function () {
202+
var inj = injector([bind(Directive).toClass(Directive)]);
203+
expect(inj.getAtIndex(0)).toBeAnInstanceOf(Directive);
204+
expect(() => inj.getAtIndex(-1)).toThrowError(
205+
'Index -1 is out-of-bounds.');
206+
expect(() => inj.getAtIndex(10)).toThrowError(
207+
'Index 10 is out-of-bounds.');
208+
});
199209
});
200210

201211
describe("special objects", function () {
@@ -207,4 +217,4 @@ export function main() {
207217
});
208218
});
209219
});
210-
}
220+
}

0 commit comments

Comments
 (0)