mirror of
https://github.com/Polymer/polymer.git
synced 2025-02-25 18:55:30 -06:00
Merge pull request #3358 from kaste/computed-bound-methods
Support dynamic functions for computed annotations.
This commit is contained in:
commit
333f082609
@ -66,6 +66,10 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
|
||||
if (args) {
|
||||
fn.apply(this, args);
|
||||
}
|
||||
} else if (effect.dynamicFn) {
|
||||
// dynamic functions can be just like every other property `undefined`
|
||||
// so we MUST ignore an undefined value here. (That's totally the
|
||||
// same guard we use within `_marshalArgs` and part of the spec.)
|
||||
} else {
|
||||
this._warn(this._logf('_complexObserverEffect', 'observer method `' +
|
||||
effect.method + '` not defined'));
|
||||
@ -73,15 +77,20 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
|
||||
},
|
||||
|
||||
_computeEffect: function(source, value, effect) {
|
||||
var args = Polymer.Bind._marshalArgs(this.__data__, effect, source, value);
|
||||
if (args) {
|
||||
var fn = this[effect.method];
|
||||
if (fn) {
|
||||
this.__setProperty(effect.name, fn.apply(this, args));
|
||||
} else {
|
||||
this._warn(this._logf('_computeEffect', 'compute method `' +
|
||||
effect.method + '` not defined'));
|
||||
var fn = this[effect.method];
|
||||
if (fn) {
|
||||
var args = Polymer.Bind._marshalArgs(this.__data__, effect, source, value);
|
||||
if (args) {
|
||||
var computedvalue = fn.apply(this, args);
|
||||
this.__setProperty(effect.name, computedvalue);
|
||||
}
|
||||
} else if (effect.dynamicFn) {
|
||||
// dynamic functions can be just like every other property `undefined`
|
||||
// so we MUST ignore an undefined value here. (That's totally the
|
||||
// same guard we use within `_marshalArgs` and part of the spec.)
|
||||
} else {
|
||||
this._warn(this._logf('_computeEffect', 'compute method `' +
|
||||
effect.method + '` not defined'));
|
||||
}
|
||||
},
|
||||
|
||||
@ -97,6 +106,10 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
|
||||
}
|
||||
this._applyEffectValue(effect, computedvalue);
|
||||
}
|
||||
} else if (effect.dynamicFn) {
|
||||
// dynamic functions can be just like every other property `undefined`
|
||||
// so we MUST ignore an undefined value here. (That's totally the
|
||||
// same guard we use within `_marshalArgs` and part of the spec.)
|
||||
} else {
|
||||
computedHost._warn(computedHost._logf('_annotatedComputationEffect',
|
||||
'compute method `' + effect.method + '` not defined'));
|
||||
@ -108,6 +121,9 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
|
||||
_marshalArgs: function(model, effect, path, value) {
|
||||
var values = [];
|
||||
var args = effect.args;
|
||||
// Actually we should return early as soon as we see an `undefined`,
|
||||
// but dom-repeat relies on this behavior.
|
||||
var bailoutEarly = (args.length > 1 || effect.dynamicFn);
|
||||
for (var i=0, l=args.length; i<l; i++) {
|
||||
var arg = args[i];
|
||||
var name = arg.name;
|
||||
@ -119,7 +135,7 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
|
||||
} else {
|
||||
v = model[name];
|
||||
}
|
||||
if (args.length > 1 && v === undefined) {
|
||||
if (bailoutEarly && v === undefined) {
|
||||
return;
|
||||
}
|
||||
if (arg.wildcard) {
|
||||
|
@ -145,8 +145,10 @@ TODO(sjmiles): this module should produce either syntactic metadata
|
||||
for (var k=0; k<b.parts.length; k++) {
|
||||
var p = b.parts[k];
|
||||
if (!p.literal) {
|
||||
p.signature = this._parseMethod(p.value);
|
||||
if (!p.signature) {
|
||||
var signature = this._parseMethod(p.value);
|
||||
if (signature) {
|
||||
p.signature = signature;
|
||||
} else {
|
||||
p.model = this._modelForPath(p.value);
|
||||
}
|
||||
}
|
||||
|
@ -79,14 +79,27 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
|
||||
|
||||
_addComputedEffect: function(name, expression) {
|
||||
var sig = this._parseMethod(expression);
|
||||
|
||||
var dynamicFn = sig.dynamicFn;
|
||||
|
||||
for (var i=0, arg; (i<sig.args.length) && (arg=sig.args[i]); i++) {
|
||||
this._addPropertyEffect(arg.model, 'compute', {
|
||||
method: sig.method,
|
||||
args: sig.args,
|
||||
trigger: arg,
|
||||
name: name
|
||||
name: name,
|
||||
dynamicFn: dynamicFn
|
||||
});
|
||||
}
|
||||
if (dynamicFn) {
|
||||
this._addPropertyEffect(sig.method, 'compute', {
|
||||
method: sig.method,
|
||||
args: sig.args,
|
||||
trigger: null,
|
||||
name: name,
|
||||
dynamicFn: dynamicFn
|
||||
})
|
||||
}
|
||||
},
|
||||
|
||||
_addObserverEffect: function(property, observer) {
|
||||
@ -111,11 +124,22 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
|
||||
throw new Error("Malformed observer expression '" + observer + "'");
|
||||
}
|
||||
|
||||
var dynamicFn = sig.dynamicFn;
|
||||
|
||||
for (var i=0, arg; (i<sig.args.length) && (arg=sig.args[i]); i++) {
|
||||
this._addPropertyEffect(arg.model, 'complexObserver', {
|
||||
method: sig.method,
|
||||
args: sig.args,
|
||||
trigger: arg
|
||||
trigger: arg,
|
||||
dynamicFn: dynamicFn
|
||||
});
|
||||
}
|
||||
if (dynamicFn) {
|
||||
this._addPropertyEffect(sig.method, 'complexObserver', {
|
||||
method: sig.method,
|
||||
args: sig.args,
|
||||
trigger: null,
|
||||
dynamicFn: dynamicFn
|
||||
});
|
||||
}
|
||||
},
|
||||
@ -171,6 +195,14 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
|
||||
arg);
|
||||
}
|
||||
}
|
||||
if (sig.dynamicFn) {
|
||||
// trigger=null is sufficient as long as we don't allow paths to be
|
||||
// used. If we change our mind, we must first implement this in the
|
||||
// effects anyway where we basically do a `fn = this[methodName]` at
|
||||
// the moment.
|
||||
this.__addAnnotatedComputationEffect(
|
||||
sig.method, index, note, part, null);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
@ -184,7 +216,8 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
|
||||
negate: part.negate,
|
||||
method: part.signature.method,
|
||||
args: part.signature.args,
|
||||
trigger: trigger
|
||||
trigger: trigger,
|
||||
dynamicFn: part.signature.dynamicFn
|
||||
});
|
||||
},
|
||||
|
||||
@ -194,6 +227,11 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
|
||||
var m = expression.match(/([^\s]+?)\(([\s\S]*)\)/);
|
||||
if (m) {
|
||||
var sig = { method: m[1], static: true };
|
||||
// TODO(kaste): Optimize/memoize `getPropertyInfo`.
|
||||
if (this.getPropertyInfo(sig.method) !== Polymer.nob) {
|
||||
sig.static = false;
|
||||
sig.dynamicFn = true;
|
||||
}
|
||||
if (m[2].trim()) {
|
||||
// replace escaped commas with comma entity, split on un-escaped commas
|
||||
var args = m[2].replace(/\\,/g, ',').split(',');
|
||||
|
@ -588,3 +588,73 @@
|
||||
})();
|
||||
</script>
|
||||
</dom-module>
|
||||
|
||||
<dom-module id="x-bind-computed-property">
|
||||
<template>
|
||||
<div id="check">[[translate('Hello World.')]]</div>
|
||||
</template>
|
||||
<script>
|
||||
(function(){
|
||||
var TranslateBehavior = {
|
||||
properties: {
|
||||
translate: {
|
||||
type: Function,
|
||||
computed: '_computeTranslateFn(translator)'
|
||||
}
|
||||
}
|
||||
};
|
||||
Polymer({
|
||||
is: 'x-bind-computed-property',
|
||||
behaviors: [TranslateBehavior],
|
||||
properties: {
|
||||
translator: {
|
||||
type: Function,
|
||||
value: function() {
|
||||
return function(message) {
|
||||
return 'translated: ' + message;
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
|
||||
_computeTranslateFn: function(translator) {
|
||||
return function(message) {
|
||||
return translator(message);
|
||||
}
|
||||
},
|
||||
|
||||
});
|
||||
})();
|
||||
</script>
|
||||
</dom-module>
|
||||
|
||||
<dom-module id="x-bind-computed-property-late-translator">
|
||||
<template>
|
||||
<div id="check">[[translate(message)]]</div>
|
||||
</template>
|
||||
<script>
|
||||
Polymer({
|
||||
is: 'x-bind-computed-property-late-translator',
|
||||
properties: {
|
||||
message: {
|
||||
type: String,
|
||||
value: 'Hello'
|
||||
},
|
||||
translate: {
|
||||
type: Function,
|
||||
computed: '_computeTranslateFn(translator)'
|
||||
},
|
||||
translator: {
|
||||
type: Function,
|
||||
},
|
||||
},
|
||||
|
||||
_computeTranslateFn: function(translator) {
|
||||
return function(message) {
|
||||
return translator(message);
|
||||
}
|
||||
},
|
||||
|
||||
});
|
||||
</script>
|
||||
</dom-module>
|
@ -285,6 +285,107 @@ suite('single-element binding effects', function() {
|
||||
|
||||
<script>
|
||||
|
||||
suite('computed bindings with dynamic functions', function() {
|
||||
|
||||
var el;
|
||||
|
||||
setup(function() {
|
||||
|
||||
});
|
||||
|
||||
teardown(function() {
|
||||
document.body.removeChild(el);
|
||||
});
|
||||
|
||||
test('annotated computation with dynamic function', function() {
|
||||
el = document.createElement('x-bind-computed-property');
|
||||
document.body.appendChild(el);
|
||||
|
||||
assert.equal(el.$.check.textContent, 'translated: Hello World.');
|
||||
|
||||
el.translator = function(message) {
|
||||
return 'changed: ' + message;
|
||||
}
|
||||
|
||||
assert.equal(el.$.check.textContent, 'changed: Hello World.');
|
||||
});
|
||||
|
||||
test('annotated computation / late resolved dynamic function', function() {
|
||||
el = document.createElement('x-bind-computed-property-late-translator');
|
||||
document.body.appendChild(el);
|
||||
|
||||
assert.equal(el.$.check.textContent.trim(), '');
|
||||
|
||||
el.translator = function(message) {
|
||||
return 'translated: ' + message;
|
||||
};
|
||||
|
||||
assert.equal(el.$.check.textContent, 'translated: Hello');
|
||||
});
|
||||
|
||||
test('observer with dynamic function', function() {
|
||||
Polymer({
|
||||
is: 'x-observer-with-dynamic-function',
|
||||
properties: {
|
||||
translate: {
|
||||
type: Function,
|
||||
},
|
||||
message: {
|
||||
type: String,
|
||||
value: 'Hello'
|
||||
},
|
||||
},
|
||||
|
||||
observers: ['translate(message)'],
|
||||
|
||||
})
|
||||
|
||||
el = document.createElement('x-observer-with-dynamic-function');
|
||||
document.body.appendChild(el);
|
||||
|
||||
var called = 0;
|
||||
el.translate = function() {
|
||||
called += 1;
|
||||
};
|
||||
|
||||
assert.equal(called, 1);
|
||||
|
||||
});
|
||||
|
||||
test('computed property with dynamic function', function() {
|
||||
Polymer({
|
||||
is: 'x-computed-property-with-dynamic-function',
|
||||
properties: {
|
||||
computedValue: {
|
||||
computed: "translate('Hello')"
|
||||
},
|
||||
translate: {
|
||||
type: Function
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
el = document.createElement('x-computed-property-with-dynamic-function');
|
||||
document.body.appendChild(el);
|
||||
|
||||
assert.equal(el.computedValue, undefined);
|
||||
|
||||
var called = 0;
|
||||
el.translate = function(message) {
|
||||
called += 1;
|
||||
return 'translated: ' + message;
|
||||
};
|
||||
|
||||
assert.equal(called, 1);
|
||||
assert.equal(el.computedValue, 'translated: Hello');
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
</script>
|
||||
|
||||
<script>
|
||||
suite('2-way binding effects between elements', function() {
|
||||
|
||||
var el;
|
||||
|
Loading…
Reference in New Issue
Block a user