Skip to content

Commit ed7d25c

Browse files
author
Steve Orvell
committed
Merge pull request #1514 from Polymer/0.8-nested-template-args
Process nested templates in base, parse method args for parentProps.
2 parents 23190b1 + 312d50a commit ed7d25c

File tree

8 files changed

+121
-87
lines changed

8 files changed

+121
-87
lines changed

src/lib/annotations/annotations.html

+2-44
Original file line numberDiff line numberDiff line change
@@ -161,11 +161,7 @@
161161

162162
// 1. Parse annotations from the template and memoize them on
163163
// content._notes (recurses into nested templates)
164-
// 2. Parse template bindings for parent.* properties and memoize them on
165-
// content._parentProps
166-
// 3. Create bindings in current scope's annotation list to template for
167-
// parent props found in template
168-
// 4. Remove template.content and store it in annotation list, where it
164+
// 2. Remove template.content and store it in annotation list, where it
169165
// will be the responsibility of the host to set it back to the template
170166
// (this is both an optimization to avoid re-stamping nested template
171167
// children and avoids a bug in Chrome where nested template children
@@ -176,56 +172,18 @@
176172
var content = document.createDocumentFragment();
177173
content._notes = this.parseAnnotations(node);
178174
content.appendChild(node.content);
179-
// Special-case treatment of 'parent.*' props for nested templates
180-
// Automatically bind `prop` on host to `_parent_prop` on template
181-
// for any `parent.prop`'s encountered in template binding; it is
182-
// responsibility of the template implementation to forward
183-
// these properties as appropriate
184-
var bindings = [];
185-
this._discoverTemplateParentProps(content);
186-
for (var prop in content._parentProps) {
187-
bindings.push({
188-
index: index,
189-
kind: 'property',
190-
mode: '{',
191-
name: '_parent_' + prop,
192-
value: prop
193-
});
194-
}
195175
// TODO(sjmiles): using `nar` to avoid unnecessary allocation;
196176
// in general the handling of these arrays needs some cleanup
197177
// in this module
198178
list.push({
199-
bindings: bindings,
179+
bindings: Polymer.nar,
200180
events: Polymer.nar,
201181
templateContent: content,
202182
parent: parent,
203183
index: index
204184
});
205185
},
206186

207-
// Finds all bindings in template content and stores the path roots in
208-
// the path members in content._parentProps. Each outer template merges
209-
// inner _parentProps to propagate inner parent property needs to outer
210-
// templates.
211-
_discoverTemplateParentProps: function(content) {
212-
var pp = content._parentProps = {};
213-
content._notes.forEach(function(n) {
214-
// Find all bindings to parent.* and spread them into _parentPropChain
215-
n.bindings.forEach(function(b) {
216-
var prop = b.value;
217-
var dot = prop.indexOf('.');
218-
prop = (dot < 0) ? prop : prop.slice(0, dot);
219-
pp[prop] = true;
220-
});
221-
// Merge child _parentProps into this _parentProps
222-
if (n.templateContent) {
223-
var tpp = n.templateContent._parentProps;
224-
Polymer.Base.mixin(pp, tpp);
225-
}
226-
});
227-
},
228-
229187
// add annotation data from attributes to the `annotation` for node `node`
230188
// TODO(sjmiles): the distinction between an `annotation` and
231189
// `annotation data` is not as clear as it could be

src/lib/template/templatizer.html

+12-20
Original file line numberDiff line numberDiff line change
@@ -124,28 +124,20 @@
124124
},
125125

126126
_customPrepAnnotations: function(archetype, template) {
127-
if (template) {
128-
archetype._template = template;
129-
var c = template._content;
130-
if (c) {
131-
var rootDataHost = archetype._rootDataHost;
132-
if (rootDataHost) {
133-
Polymer.Annotations.prepElement =
134-
rootDataHost._prepElement.bind(rootDataHost);
135-
}
136-
archetype._notes = c._notes ||
137-
Polymer.Annotations.parseAnnotations(template);
138-
c._notes = archetype._notes;
139-
Polymer.Annotations.prepElement = null;
140-
archetype._parentProps = c._parentProps;
141-
}
142-
else {
143-
console.warn('no _content');
127+
archetype._template = template;
128+
var c = template._content;
129+
if (!c._notes) {
130+
var rootDataHost = archetype._rootDataHost;
131+
if (rootDataHost) {
132+
Polymer.Annotations.prepElement =
133+
rootDataHost._prepElement.bind(rootDataHost);
144134
}
135+
c._notes = Polymer.Annotations.parseAnnotations(template);
136+
Polymer.Annotations.prepElement = null;
137+
this._processAnnotations(c._notes);
145138
}
146-
else {
147-
console.warn('no _template');
148-
}
139+
archetype._notes = c._notes;
140+
archetype._parentProps = c._parentProps;
149141
},
150142

151143
// Sets up accessors on the template to call abstract _forwardParentProp

src/standard/annotations.html

+60
Original file line numberDiff line numberDiff line change
@@ -124,10 +124,70 @@
124124
// TODO(sorvell): ad hoc method of plugging behavior into Annotations
125125
Polymer.Annotations.prepElement = this._prepElement.bind(this);
126126
this._notes = Polymer.Annotations.parseAnnotations(this._template);
127+
this._processAnnotations(this._notes);
127128
Polymer.Annotations.prepElement = null;
128129
}
129130
},
130131

132+
_processAnnotations: function(notes) {
133+
for (var i=0; i<notes.length; i++) {
134+
var note = notes[i];
135+
// Parse bindings for methods & path roots (models)
136+
for (var j=0; j<note.bindings.length; j++) {
137+
var b = note.bindings[j];
138+
b.signature = this._parseMethod(b.value);
139+
if (!b.signature) {
140+
b.model = this._modelForPath(b.value);
141+
}
142+
}
143+
// Recurse into nested templates & bind parent props
144+
if (note.templateContent) {
145+
this._processAnnotations(note.templateContent._notes);
146+
var pp = note.templateContent._parentProps =
147+
this._discoverTemplateParentProps(note.templateContent._notes);
148+
var bindings = [];
149+
for (var prop in pp) {
150+
bindings.push({
151+
index: note.index,
152+
kind: 'property',
153+
mode: '{',
154+
name: '_parent_' + prop,
155+
model: prop,
156+
value: prop
157+
});
158+
}
159+
note.bindings = note.bindings.concat(bindings);
160+
}
161+
}
162+
},
163+
164+
// Finds all bindings in template content and stores the path roots in
165+
// the path members in content._parentProps. Each outer template merges
166+
// inner _parentProps to propagate inner parent property needs to outer
167+
// templates.
168+
_discoverTemplateParentProps: function(notes) {
169+
var pp = {};
170+
notes.forEach(function(n) {
171+
// Find all bindings to parent.* and spread them into _parentPropChain
172+
n.bindings.forEach(function(b) {
173+
if (b.signature) {
174+
var args = b.signature.args;
175+
for (var k=0; k<args.length; k++) {
176+
pp[args[k].model] = true;
177+
}
178+
} else {
179+
pp[b.model] = true;
180+
}
181+
});
182+
// Merge child _parentProps into this _parentProps
183+
if (n.templateContent) {
184+
var tpp = n.templateContent._parentProps;
185+
Polymer.Base.mixin(pp, tpp);
186+
}
187+
});
188+
return pp;
189+
},
190+
131191
_prepElement: function(element) {
132192
Polymer.ResolveUrl.resolveAttrs(element, this._template.ownerDocument);
133193
},

src/standard/effects.html

+14-16
Original file line numberDiff line numberDiff line change
@@ -33,10 +33,7 @@
3333
Polymer.Base._addFeature({
3434

3535
_addPropertyEffect: function(property, kind, effect) {
36-
// TODO(sjmiles): everything to the right of the first '.' is lost, implies
37-
// there is some duplicate information flow (not the only sign)
38-
var model = property.split('.').shift();
39-
Polymer.Bind.addPropertyEffect(this, model, kind, effect);
36+
Polymer.Bind.addPropertyEffect(this, property, kind, effect);
4037
},
4138

4239
// prototyping
@@ -79,13 +76,16 @@
7976
if (m) {
8077
return {
8178
method: m[1],
82-
args: m[2].split(/[^\w.*]+/).map(this._parseArg)
79+
args: m[2].split(/[^\w.*]+/).map(this._parseArg, this)
8380
};
8481
}
8582
},
8683

8784
_parseArg: function(arg) {
88-
var a = { name: arg };
85+
var a = {
86+
name: arg,
87+
model: this._modelForPath(arg)
88+
};
8989
a.structured = arg.indexOf('.') > 0;
9090
if (a.structured) {
9191
a.wildcard = (arg.slice(-2) == '.*');
@@ -99,7 +99,7 @@
9999
_addComputedEffect: function(name, expression) {
100100
var sig = this._parseMethod(expression);
101101
sig.args.forEach(function(arg) {
102-
this._addPropertyEffect(arg.name, 'compute', {
102+
this._addPropertyEffect(arg.model, 'compute', {
103103
method: sig.method,
104104
args: sig.args,
105105
arg: arg,
@@ -126,7 +126,7 @@
126126
_addComplexObserverEffect: function(observer) {
127127
var sig = this._parseMethod(observer);
128128
sig.args.forEach(function(arg) {
129-
this._addPropertyEffect(arg.name, 'complexObserver', {
129+
this._addPropertyEffect(arg.model, 'complexObserver', {
130130
method: sig.method,
131131
args: sig.args,
132132
arg: arg
@@ -154,22 +154,20 @@
154154
Polymer.Bind._addAnnotatedListener(this, index,
155155
note.name, note.value, note.event);
156156
}
157-
var sig = this._parseMethod(note.value);
158-
if (sig) {
159-
this._addAnnotatedComputationEffect(sig, note, index);
157+
if (note.signature) {
158+
this._addAnnotatedComputationEffect(note, index);
160159
} else {
161160
// capture the node index
162161
note.index = index;
163-
// discover top-level property (model) from path
164-
var model = note.value.split('.').shift();
165162
// add 'annotation' binding effect for property 'model'
166-
this._addPropertyEffect(model, 'annotation', note);
163+
this._addPropertyEffect(note.model, 'annotation', note);
167164
}
168165
},
169166

170-
_addAnnotatedComputationEffect: function(sig, note, index) {
167+
_addAnnotatedComputationEffect: function(note, index) {
168+
var sig = note.signature;
171169
sig.args.forEach(function(arg) {
172-
this._addPropertyEffect(arg.name, 'annotatedComputation', {
170+
this._addPropertyEffect(arg.model, 'annotatedComputation', {
173171
kind: note.kind,
174172
method: sig.method,
175173
args: sig.args,

src/standard/notify-path.html

+2-1
Original file line numberDiff line numberDiff line change
@@ -271,7 +271,8 @@
271271
},
272272

273273
_modelForPath: function(path) {
274-
return path.split('.').shift();
274+
var dot = path.indexOf('.');
275+
return (dot < 0) ? path : path.slice(0, dot);
275276
},
276277

277278
_EVENT_CHANGED: '-changed',

test/smoke/dom-repeat.html

+6-2
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,7 @@
9191
<x-labeled-input label="outerObjA.value" value="{{outerObjA.value}}"></x-labeled-input>
9292
<x-labeled-input label="outerObjB.value" value="{{outerObjB.value}}"></x-labeled-input>
9393
<x-labeled-input label="outerObjC.value" value="{{outerObjC.value}}"></x-labeled-input>
94+
<x-labeled-input label="outerObjD.value" value="{{outerObjD.value}}"></x-labeled-input>
9495
</div>
9596

9697
<div class="layout horizontal flex">
@@ -104,7 +105,7 @@
104105
<x-labeled-input label="itemA.name" value="{{itemA.name}}"></x-labeled-input>
105106
<x-labeled-input label="outerA" value="{{outerA}}"></x-labeled-input>
106107
<x-labeled-input label="outerObjA.value" value="{{outerObjA.value}}"></x-labeled-input>
107-
<x-labeled-input label="computeConcat(itemA.name, outerObjA.value)" value="{{computeConcat(itemA.name, outerObjA.value)}}" disabled></x-labeled-input>
108+
<x-labeled-input label="computeConcat(itemA.name, outerObjD.value)" value="{{computeConcat(itemA.name, outerObjD.value)}}" disabled></x-labeled-input>
108109
<template is="dom-repeat" id="xr1-2" idx$="{{index}}" items="{{itemA.items}}" as="itemB">
109110
<div class="item" id="xr1-2" idx$="{{index}}">
110111
<div class="index">idx: <span>{{index}}</span></div>
@@ -143,7 +144,7 @@
143144
<x-labeled-input label="itemA.name" value="{{itemA.name}}"></x-labeled-input>
144145
<x-labeled-input label="outerA" value="{{outerA}}"></x-labeled-input>
145146
<x-labeled-input label="outerObjA.value" value="{{outerObjA.value}}"></x-labeled-input>
146-
<x-labeled-input label="computeConcat(itemA.name, outerObjA.value)" value="{{computeConcat(itemA.name, outerObjA.value)}}" disabled></x-labeled-input>
147+
<x-labeled-input label="computeConcat(itemA.name, outerObjD.value)" value="{{computeConcat(itemA.name, outerObjD.value)}}" disabled></x-labeled-input>
147148
<template is="dom-repeat" id="xr2-2" idx$="{{index}}" items="{{itemA.items}}" as="itemB">
148149
<div class="item" id="xr2-2" idx$="{{index}}">
149150
<div class="index">idx: <span>{{index}}</span></div>
@@ -297,6 +298,9 @@
297298
},
298299
outerObjC: {
299300
value: function() { return { value: 'outerObjC.value' }; }
301+
},
302+
outerObjD: {
303+
value: function() { return { value: 'outerObjD.value' }; }
300304
}
301305
},
302306
changeOuterA: function() {

test/unit/dom-repeat-elements.html

+22-4
Original file line numberDiff line numberDiff line change
@@ -114,7 +114,10 @@
114114
innerb-prop="{{innerbProp}}"
115115
itemb-prop="{{itembProp}}"
116116
innerc-prop="{{innercProp}}"
117-
itemc-prop="{{itemcProp}}">
117+
itemc-prop="{{itemcProp}}"
118+
computed1="{{computed1}}"
119+
computed2="{{computed2}}"
120+
computed3="{{computed3}}">
118121
</x-bar>
119122
</template>
120123
</dom-module>
@@ -267,7 +270,8 @@
267270
itema-prop="{{itema.prop}}"
268271
outer-prop="{{prop}}"
269272
outer-item-prop="{{item.prop}}"
270-
indexa="{{indexa}}">
273+
indexa="{{indexa}}"
274+
computeda="{{concat(itema.prop, itemForComputedA.prop)}}">
271275
</x-foo>
272276
<template is="dom-repeat" items="{{itema.items}}" as="itemb" index-as="indexb">
273277
<x-foo
@@ -278,7 +282,8 @@
278282
outer-prop="{{prop}}"
279283
outer-item-prop="{{item.prop}}"
280284
indexa="{{indexa}}"
281-
indexb="{{indexb}}">
285+
indexb="{{indexb}}"
286+
computedb="{{concat(itemb.prop, itemForComputedB.prop)}}">
282287
</x-foo>
283288
<template is="dom-repeat" items="{{itemb.items}}" as="itemc" index-as="indexc">
284289
<x-foo
@@ -292,7 +297,8 @@
292297
outer-item-prop="{{item.prop}}"
293298
indexa="{{indexa}}"
294299
indexb="{{indexb}}"
295-
indexc="{{indexc}}">
300+
indexc="{{indexc}}"
301+
computedc="{{concat(itemc.prop, itemForComputedC.prop)}}">
296302
</x-foo>
297303
</template>
298304
</template>
@@ -312,6 +318,15 @@
312318
item: {
313319
value: function() { return {prop: 'outerItem'}; }
314320
},
321+
itemForComputedA: {
322+
value: function() { return {prop: 'itemForComputedA'}; }
323+
},
324+
itemForComputedB: {
325+
value: function() { return {prop: 'itemForComputedB'}; }
326+
},
327+
itemForComputedC: {
328+
value: function() { return {prop: 'itemForComputedC'}; }
329+
},
315330
_testHost: {
316331
value: function() { return this; }
317332
}
@@ -323,6 +338,9 @@
323338
filter2nd: function(o) {
324339
assert.equal(this, this._testHost);
325340
return o.prop.indexOf('2') < 0;
341+
},
342+
concat: function(a, b) {
343+
return a + '+' + b;
326344
}
327345
});
328346
</script>

test/unit/dom-repeat.html

+3
Original file line numberDiff line numberDiff line change
@@ -87,13 +87,16 @@ <h4>inDocumentRepeater</h4>
8787
var stamped = Polymer.dom(configured.root).querySelectorAll('*:not(template)');
8888
assert.equal(stamped.length, 3 + 3*3 + 3*3*3, 'total stamped count incorrect');
8989
assert.equal(stamped[0].itemaProp, 'prop-1');
90+
assert.equal(stamped[0].computeda, 'prop-1+itemForComputedA');
9091
assert.equal(stamped[0].indexa, 0);
9192
assert.equal(stamped[0].$.bar.itemaProp, 'prop-1');
9293
assert.equal(stamped[1].itembProp, 'prop-1-1');
94+
assert.equal(stamped[1].computedb, 'prop-1-1+itemForComputedB');
9395
assert.equal(stamped[1].indexa, 0);
9496
assert.equal(stamped[1].indexb, 0);
9597
assert.equal(stamped[1].$.bar.itembProp, 'prop-1-1');
9698
assert.equal(stamped[2].itemcProp, 'prop-1-1-1');
99+
assert.equal(stamped[2].computedc, 'prop-1-1-1+itemForComputedC');
97100
assert.equal(stamped[2].indexa, 0);
98101
assert.equal(stamped[2].indexb, 0);
99102
assert.equal(stamped[2].indexc, 0);

0 commit comments

Comments
 (0)