Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Process nested templates in base, parse method args for parentProps. #1514

Merged
merged 1 commit into from
May 12, 2015
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
46 changes: 2 additions & 44 deletions src/lib/annotations/annotations.html
Original file line number Diff line number Diff line change
Expand Up @@ -161,11 +161,7 @@

// 1. Parse annotations from the template and memoize them on
// content._notes (recurses into nested templates)
// 2. Parse template bindings for parent.* properties and memoize them on
// content._parentProps
// 3. Create bindings in current scope's annotation list to template for
// parent props found in template
// 4. Remove template.content and store it in annotation list, where it
// 2. Remove template.content and store it in annotation list, where it
// will be the responsibility of the host to set it back to the template
// (this is both an optimization to avoid re-stamping nested template
// children and avoids a bug in Chrome where nested template children
Expand All @@ -176,56 +172,18 @@
var content = document.createDocumentFragment();
content._notes = this.parseAnnotations(node);
content.appendChild(node.content);
// Special-case treatment of 'parent.*' props for nested templates
// Automatically bind `prop` on host to `_parent_prop` on template
// for any `parent.prop`'s encountered in template binding; it is
// responsibility of the template implementation to forward
// these properties as appropriate
var bindings = [];
this._discoverTemplateParentProps(content);
for (var prop in content._parentProps) {
bindings.push({
index: index,
kind: 'property',
mode: '{',
name: '_parent_' + prop,
value: prop
});
}
// TODO(sjmiles): using `nar` to avoid unnecessary allocation;
// in general the handling of these arrays needs some cleanup
// in this module
list.push({
bindings: bindings,
bindings: Polymer.nar,
events: Polymer.nar,
templateContent: content,
parent: parent,
index: index
});
},

// Finds all bindings in template content and stores the path roots in
// the path members in content._parentProps. Each outer template merges
// inner _parentProps to propagate inner parent property needs to outer
// templates.
_discoverTemplateParentProps: function(content) {
var pp = content._parentProps = {};
content._notes.forEach(function(n) {
// Find all bindings to parent.* and spread them into _parentPropChain
n.bindings.forEach(function(b) {
var prop = b.value;
var dot = prop.indexOf('.');
prop = (dot < 0) ? prop : prop.slice(0, dot);
pp[prop] = true;
});
// Merge child _parentProps into this _parentProps
if (n.templateContent) {
var tpp = n.templateContent._parentProps;
Polymer.Base.mixin(pp, tpp);
}
});
},

// add annotation data from attributes to the `annotation` for node `node`
// TODO(sjmiles): the distinction between an `annotation` and
// `annotation data` is not as clear as it could be
Expand Down
32 changes: 12 additions & 20 deletions src/lib/template/templatizer.html
Original file line number Diff line number Diff line change
Expand Up @@ -124,28 +124,20 @@
},

_customPrepAnnotations: function(archetype, template) {
if (template) {
archetype._template = template;
var c = template._content;
if (c) {
var rootDataHost = archetype._rootDataHost;
if (rootDataHost) {
Polymer.Annotations.prepElement =
rootDataHost._prepElement.bind(rootDataHost);
}
archetype._notes = c._notes ||
Polymer.Annotations.parseAnnotations(template);
c._notes = archetype._notes;
Polymer.Annotations.prepElement = null;
archetype._parentProps = c._parentProps;
}
else {
console.warn('no _content');
archetype._template = template;
var c = template._content;
if (!c._notes) {
var rootDataHost = archetype._rootDataHost;
if (rootDataHost) {
Polymer.Annotations.prepElement =
rootDataHost._prepElement.bind(rootDataHost);
}
c._notes = Polymer.Annotations.parseAnnotations(template);
Polymer.Annotations.prepElement = null;
this._processAnnotations(c._notes);
}
else {
console.warn('no _template');
}
archetype._notes = c._notes;
archetype._parentProps = c._parentProps;
},

// Sets up accessors on the template to call abstract _forwardParentProp
Expand Down
60 changes: 60 additions & 0 deletions src/standard/annotations.html
Original file line number Diff line number Diff line change
Expand Up @@ -124,10 +124,70 @@
// TODO(sorvell): ad hoc method of plugging behavior into Annotations
Polymer.Annotations.prepElement = this._prepElement.bind(this);
this._notes = Polymer.Annotations.parseAnnotations(this._template);
this._processAnnotations(this._notes);
Polymer.Annotations.prepElement = null;
}
},

_processAnnotations: function(notes) {
for (var i=0; i<notes.length; i++) {
var note = notes[i];
// Parse bindings for methods & path roots (models)
for (var j=0; j<note.bindings.length; j++) {
var b = note.bindings[j];
b.signature = this._parseMethod(b.value);
if (!b.signature) {
b.model = this._modelForPath(b.value);
}
}
// Recurse into nested templates & bind parent props
if (note.templateContent) {
this._processAnnotations(note.templateContent._notes);
var pp = note.templateContent._parentProps =
this._discoverTemplateParentProps(note.templateContent._notes);
var bindings = [];
for (var prop in pp) {
bindings.push({
index: note.index,
kind: 'property',
mode: '{',
name: '_parent_' + prop,
model: prop,
value: prop
});
}
note.bindings = note.bindings.concat(bindings);
}
}
},

// Finds all bindings in template content and stores the path roots in
// the path members in content._parentProps. Each outer template merges
// inner _parentProps to propagate inner parent property needs to outer
// templates.
_discoverTemplateParentProps: function(notes) {
var pp = {};
notes.forEach(function(n) {
// Find all bindings to parent.* and spread them into _parentPropChain
n.bindings.forEach(function(b) {
if (b.signature) {
var args = b.signature.args;
for (var k=0; k<args.length; k++) {
pp[args[k].model] = true;
}
} else {
pp[b.model] = true;
}
});
// Merge child _parentProps into this _parentProps
if (n.templateContent) {
var tpp = n.templateContent._parentProps;
Polymer.Base.mixin(pp, tpp);
}
});
return pp;
},

_prepElement: function(element) {
Polymer.ResolveUrl.resolveAttrs(element, this._template.ownerDocument);
},
Expand Down
30 changes: 14 additions & 16 deletions src/standard/effects.html
Original file line number Diff line number Diff line change
Expand Up @@ -33,10 +33,7 @@
Polymer.Base._addFeature({

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

// prototyping
Expand Down Expand Up @@ -79,13 +76,16 @@
if (m) {
return {
method: m[1],
args: m[2].split(/[^\w.*]+/).map(this._parseArg)
args: m[2].split(/[^\w.*]+/).map(this._parseArg, this)
};
}
},

_parseArg: function(arg) {
var a = { name: arg };
var a = {
name: arg,
model: this._modelForPath(arg)
};
a.structured = arg.indexOf('.') > 0;
if (a.structured) {
a.wildcard = (arg.slice(-2) == '.*');
Expand All @@ -99,7 +99,7 @@
_addComputedEffect: function(name, expression) {
var sig = this._parseMethod(expression);
sig.args.forEach(function(arg) {
this._addPropertyEffect(arg.name, 'compute', {
this._addPropertyEffect(arg.model, 'compute', {
method: sig.method,
args: sig.args,
arg: arg,
Expand All @@ -126,7 +126,7 @@
_addComplexObserverEffect: function(observer) {
var sig = this._parseMethod(observer);
sig.args.forEach(function(arg) {
this._addPropertyEffect(arg.name, 'complexObserver', {
this._addPropertyEffect(arg.model, 'complexObserver', {
method: sig.method,
args: sig.args,
arg: arg
Expand Down Expand Up @@ -154,22 +154,20 @@
Polymer.Bind._addAnnotatedListener(this, index,
note.name, note.value, note.event);
}
var sig = this._parseMethod(note.value);
if (sig) {
this._addAnnotatedComputationEffect(sig, note, index);
if (note.signature) {
this._addAnnotatedComputationEffect(note, index);
} else {
// capture the node index
note.index = index;
// discover top-level property (model) from path
var model = note.value.split('.').shift();
// add 'annotation' binding effect for property 'model'
this._addPropertyEffect(model, 'annotation', note);
this._addPropertyEffect(note.model, 'annotation', note);
}
},

_addAnnotatedComputationEffect: function(sig, note, index) {
_addAnnotatedComputationEffect: function(note, index) {
var sig = note.signature;
sig.args.forEach(function(arg) {
this._addPropertyEffect(arg.name, 'annotatedComputation', {
this._addPropertyEffect(arg.model, 'annotatedComputation', {
kind: note.kind,
method: sig.method,
args: sig.args,
Expand Down
3 changes: 2 additions & 1 deletion src/standard/notify-path.html
Original file line number Diff line number Diff line change
Expand Up @@ -271,7 +271,8 @@
},

_modelForPath: function(path) {
return path.split('.').shift();
var dot = path.indexOf('.');
return (dot < 0) ? path : path.slice(0, dot);
},

_EVENT_CHANGED: '-changed',
Expand Down
8 changes: 6 additions & 2 deletions test/smoke/dom-repeat.html
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,7 @@
<x-labeled-input label="outerObjA.value" value="{{outerObjA.value}}"></x-labeled-input>
<x-labeled-input label="outerObjB.value" value="{{outerObjB.value}}"></x-labeled-input>
<x-labeled-input label="outerObjC.value" value="{{outerObjC.value}}"></x-labeled-input>
<x-labeled-input label="outerObjD.value" value="{{outerObjD.value}}"></x-labeled-input>
</div>

<div class="layout horizontal flex">
Expand All @@ -104,7 +105,7 @@
<x-labeled-input label="itemA.name" value="{{itemA.name}}"></x-labeled-input>
<x-labeled-input label="outerA" value="{{outerA}}"></x-labeled-input>
<x-labeled-input label="outerObjA.value" value="{{outerObjA.value}}"></x-labeled-input>
<x-labeled-input label="computeConcat(itemA.name, outerObjA.value)" value="{{computeConcat(itemA.name, outerObjA.value)}}" disabled></x-labeled-input>
<x-labeled-input label="computeConcat(itemA.name, outerObjD.value)" value="{{computeConcat(itemA.name, outerObjD.value)}}" disabled></x-labeled-input>
<template is="dom-repeat" id="xr1-2" idx$="{{index}}" items="{{itemA.items}}" as="itemB">
<div class="item" id="xr1-2" idx$="{{index}}">
<div class="index">idx: <span>{{index}}</span></div>
Expand Down Expand Up @@ -143,7 +144,7 @@
<x-labeled-input label="itemA.name" value="{{itemA.name}}"></x-labeled-input>
<x-labeled-input label="outerA" value="{{outerA}}"></x-labeled-input>
<x-labeled-input label="outerObjA.value" value="{{outerObjA.value}}"></x-labeled-input>
<x-labeled-input label="computeConcat(itemA.name, outerObjA.value)" value="{{computeConcat(itemA.name, outerObjA.value)}}" disabled></x-labeled-input>
<x-labeled-input label="computeConcat(itemA.name, outerObjD.value)" value="{{computeConcat(itemA.name, outerObjD.value)}}" disabled></x-labeled-input>
<template is="dom-repeat" id="xr2-2" idx$="{{index}}" items="{{itemA.items}}" as="itemB">
<div class="item" id="xr2-2" idx$="{{index}}">
<div class="index">idx: <span>{{index}}</span></div>
Expand Down Expand Up @@ -297,6 +298,9 @@
},
outerObjC: {
value: function() { return { value: 'outerObjC.value' }; }
},
outerObjD: {
value: function() { return { value: 'outerObjD.value' }; }
}
},
changeOuterA: function() {
Expand Down
26 changes: 22 additions & 4 deletions test/unit/dom-repeat-elements.html
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,10 @@
innerb-prop="{{innerbProp}}"
itemb-prop="{{itembProp}}"
innerc-prop="{{innercProp}}"
itemc-prop="{{itemcProp}}">
itemc-prop="{{itemcProp}}"
computed1="{{computed1}}"
computed2="{{computed2}}"
computed3="{{computed3}}">
</x-bar>
</template>
</dom-module>
Expand Down Expand Up @@ -267,7 +270,8 @@
itema-prop="{{itema.prop}}"
outer-prop="{{prop}}"
outer-item-prop="{{item.prop}}"
indexa="{{indexa}}">
indexa="{{indexa}}"
computeda="{{concat(itema.prop, itemForComputedA.prop)}}">
</x-foo>
<template is="dom-repeat" items="{{itema.items}}" as="itemb" index-as="indexb">
<x-foo
Expand All @@ -278,7 +282,8 @@
outer-prop="{{prop}}"
outer-item-prop="{{item.prop}}"
indexa="{{indexa}}"
indexb="{{indexb}}">
indexb="{{indexb}}"
computedb="{{concat(itemb.prop, itemForComputedB.prop)}}">
</x-foo>
<template is="dom-repeat" items="{{itemb.items}}" as="itemc" index-as="indexc">
<x-foo
Expand All @@ -292,7 +297,8 @@
outer-item-prop="{{item.prop}}"
indexa="{{indexa}}"
indexb="{{indexb}}"
indexc="{{indexc}}">
indexc="{{indexc}}"
computedc="{{concat(itemc.prop, itemForComputedC.prop)}}">
</x-foo>
</template>
</template>
Expand All @@ -312,6 +318,15 @@
item: {
value: function() { return {prop: 'outerItem'}; }
},
itemForComputedA: {
value: function() { return {prop: 'itemForComputedA'}; }
},
itemForComputedB: {
value: function() { return {prop: 'itemForComputedB'}; }
},
itemForComputedC: {
value: function() { return {prop: 'itemForComputedC'}; }
},
_testHost: {
value: function() { return this; }
}
Expand All @@ -323,6 +338,9 @@
filter2nd: function(o) {
assert.equal(this, this._testHost);
return o.prop.indexOf('2') < 0;
},
concat: function(a, b) {
return a + '+' + b;
}
});
</script>
3 changes: 3 additions & 0 deletions test/unit/dom-repeat.html
Original file line number Diff line number Diff line change
Expand Up @@ -87,13 +87,16 @@ <h4>inDocumentRepeater</h4>
var stamped = Polymer.dom(configured.root).querySelectorAll('*:not(template)');
assert.equal(stamped.length, 3 + 3*3 + 3*3*3, 'total stamped count incorrect');
assert.equal(stamped[0].itemaProp, 'prop-1');
assert.equal(stamped[0].computeda, 'prop-1+itemForComputedA');
assert.equal(stamped[0].indexa, 0);
assert.equal(stamped[0].$.bar.itemaProp, 'prop-1');
assert.equal(stamped[1].itembProp, 'prop-1-1');
assert.equal(stamped[1].computedb, 'prop-1-1+itemForComputedB');
assert.equal(stamped[1].indexa, 0);
assert.equal(stamped[1].indexb, 0);
assert.equal(stamped[1].$.bar.itembProp, 'prop-1-1');
assert.equal(stamped[2].itemcProp, 'prop-1-1-1');
assert.equal(stamped[2].computedc, 'prop-1-1-1+itemForComputedC');
assert.equal(stamped[2].indexa, 0);
assert.equal(stamped[2].indexb, 0);
assert.equal(stamped[2].indexc, 0);
Expand Down