diff --git a/PRIMER.md b/PRIMER.md
index 908abc18..538fcce8 100644
--- a/PRIMER.md
+++ b/PRIMER.md
@@ -1103,7 +1103,7 @@ Values will be serialized according to type: Arrays/Objects will be `JSON.string
## Computed properties
-Polymer supports virtual properties whose values are calculated from other properties. Computed properties can be defined by providing an object-valued `computed` property on the prototype that maps property names to computing functions. The name of the function to compute the value is provided as a string with dependent properties as arguments in parenthesis. Only one dependency is supported at this time.
+Polymer supports virtual properties whose values are calculated from other properties. Computed properties can be defined by providing an object-valued `computed` property on the prototype that maps property names to computing functions. The name of the function to compute the value is provided as a string with dependent properties as arguments in parenthesis. The function will be called once (asynchronously) for any change to the dependent properties.
```html
@@ -1118,13 +1118,13 @@ Polymer supports virtual properties whose values are calculated from other prope
is: 'x-custom',
computed: {
- // when `user` changes `computeFullName` is called and the
- // value it returns is stored as `fullName`
- fullName: 'computeFullName(user)',
+ // when `first` or `last` changes `computeFullName` is called once
+ // (asynchronously) and the value it returns is stored as `fullName`
+ fullName: 'computeFullName(first, last)',
},
- computeFullName: function(user) {
- return user.firstName + ' ' + user.lastName;
+ computeFullName: function(first, last) {
+ return first + ' ' + last;
}
...
@@ -1133,6 +1133,8 @@ Polymer supports virtual properties whose values are calculated from other prope
```
+Note: Only direct properties of the element (as opposed to sub-properties of an object) can be used as dependencies at this time.
+
## Read-only properties
@@ -1304,50 +1306,11 @@ Current limitations that are on the backlog for evaluation/improvement are liste
* Support for compound property binding
* See below
-## Compound property effects
+## Compound observation
-Polymer 0.8 currently has no built-in support for compound observation or compound binding expressions. This problem space is on the backlog to be tackled in the near future. This section will discuss lower-level tools that are available in 0.8 that can be used instead.
+Polymer 0.8 does not currently support observer functions called once for changes to a set of dependent properties, outside of computed properties. If the work of computing the property is expensive, or if the side-effects of the binding are expensive, then you may want to ensure side-effects only occur once for any number of changes to them during a turn by manually introducing asynchronicity. The `computed` property feature uses `debounce` under the hood to achieve the same effect.
-Assume an element has a boolean property that should be set when either of two conditions are true: e.g. when `.isManager == true` OR `.mode == 2`, you want to set `.disabled = true`.
-
-The most naive way to achieve this in 0.8 is with separate change handlers for the dependent properties that set a `shouldDisable` property bound to the `my-child`.
-
-Example:
-
-```html
-
-
-
-
-
-
-
-```
-
-Due to the synchronous nature of bindings in 0.8, code such as the following will result in `.disabled` being set twice (and any side-effects of that property changing to potentially occur twice):
-
-```js
-myParent.isManager = false;
-myParent.mode = 5;
-```
-
-If the work of computing the property is expensive, or if the side-effects of the binding are expensive, then you may want to ensure side-effects only occur once for any number of changes to them during a turn by manually introducing asynchronicity. The `debounce` API on the Polymer Base prototype can be used to achieve this. The `debounce` API takes a signal name (String), callback, and optional wait time, and only calls the callback once for any number `debounce` calls with the same `signalName` started within the wait period.
+The `debounce` API on the Polymer Base prototype can be used to achieve this. The `debounce` API takes a signal name (String), callback, and optional wait time, and only calls the callback once for any number `debounce` calls with the same `signalName` started within the wait period.
Example:
diff --git a/src/lib/bind/bind-effects.html b/src/lib/bind/bind-effects.html
index c037ea22..eab77273 100644
--- a/src/lib/bind/bind-effects.html
+++ b/src/lib/bind/bind-effects.html
@@ -16,10 +16,13 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
var method = expression.slice(0, index);
var args = expression.slice(index + 1, -1).replace(/ /g, '').split(',');
//console.log('%c on [%s] compute [%s] via [%s]', 'color: green', args[0], name, method);
- this.addPropertyEffect(model, args[0], 'compute', {
- property: name,
- method: method
- });
+ var methodArgs = 'this._data.' + args.join(', this._data.');
+ var methodString = 'this.debounce(\'_' + method + '\', function() {\n' +
+ '\t\tthis.' + name + ' = this.' + method + '(' + methodArgs + ');\n' +
+ '\t});';
+ for (var i=0; i
diff --git a/test/unit/bind.html b/test/unit/bind.html
index 491b3088..56eed767 100644
--- a/test/unit/bind.html
+++ b/test/unit/bind.html
@@ -58,10 +58,13 @@ suite('single-element binding effects', function() {
assert.equal(called, true, 'Change handler not called');
});
- test('computed value updates', function() {
+ test('computed value updates', function(done) {
el.value = 44;
- assert.equal(el.computedvalue, 45, 'Computed value not correct');
- assert.equal(el.$.boundChild.computedvalue, 45, 'Computed value not propagated to bound child');
+ setTimeout(function() {
+ assert.equal(el.computedvalue, 45, 'Computed value not correct');
+ assert.equal(el.$.boundChild.computedvalue, 45, 'Computed value not propagated to bound child');
+ done();
+ });
});
test('notification sent', function() {
@@ -79,23 +82,49 @@ suite('single-element binding effects', function() {
assert.equal(notified, 2, 'Notification events not sent');
});
- test('computed change handler called', function() {
+ test('computed change handler called', function(done) {
var called = false;
el.computedvalueChanged = function() {
called = true;
};
el.value = 46;
- assert.equal(called, true, 'Change handler not called');
+ setTimeout(function() {
+ assert.equal(called, true, 'Change handler not called');
+ done();
+ });
});
- test('computed notification sent', function() {
+ test('computed notification sent', function(done) {
var notified = false;
el.addEventListener('computednotifyingvalue-changed', function(e) {
assert.equal(e.detail.value, 49);
notified = true;
});
el.notifyingvalue = 47;
- assert.equal(notified, true, 'Notification event not sent');
+ setTimeout(function() {
+ assert.equal(notified, true, 'Notification event not sent');
+ done();
+ });
+ });
+
+ test('computed property with multiple dependencies', function(done) {
+ var called = false;
+ el.computedFromMultipleValuesChanged = function() {
+ called = true;
+ };
+ var notified = false;
+ el.addEventListener('computed-from-multiple-values-changed', function(e) {
+ notified = true;
+ });
+ el.sum1 = 10;
+ el.sum2 = 20;
+ el.divide = 2;
+ setTimeout(function() {
+ assert.equal(el.computedFromMultipleValues, 15, 'Computed value wrong');
+ assert.equal(notified, true, 'Notification event not sent');
+ assert.equal(called, true, 'Change handler not called');
+ done();
+ });
});
test('no read-only change handler called with assignment', function() {
@@ -172,19 +201,25 @@ suite('2-way binding effects between elements', function() {
assert.equal(called, false, 'changed handler for property bound to non-notifying property called and should not have been');
});
- test('binding to non-notifying computed property', function() {
+ test('binding to non-notifying computed property', function(done) {
el.boundcomputedvalue = 42;
el.$.basic1.value = 43;
- assert.equal(el.boundcomputedvalue, 42, 'binding to non-notifying computed property updated and should not have been');
+ setTimeout(function() {
+ assert.equal(el.boundcomputedvalue, 42, 'binding to non-notifying computed property updated and should not have been');
+ done();
+ });
});
- test('changed handler for property bound to non-notifying computed property', function() {
+ test('changed handler for property bound to non-notifying computed property', function(done) {
var called = false;
el.boundcomputedvalueChanged = function() {
called = true;
};
el.$.basic1.value = 44;
- assert.equal(called, false, 'changed handler for property bound to non-notifying computed property called and should not have been');
+ setTimeout(function() {
+ assert.equal(called, false, 'changed handler for property bound to non-notifying computed property called and should not have been');
+ done();
+ });
});
test('binding to notifying property', function() {
@@ -206,18 +241,24 @@ suite('2-way binding effects between elements', function() {
assert.equal(called, true, 'changed handler for property bound to notifying property not called');
});
- test('binding to notifying computed property', function() {
+ test('binding to notifying computed property', function(done) {
el.$.basic1.notifyingvalue = 43;
- assert.equal(el.boundcomputednotifyingvalue, 45, 'binding to notifying computed property not updated');
+ setTimeout(function() {
+ assert.equal(el.boundcomputednotifyingvalue, 45, 'binding to notifying computed property not updated');
+ done();
+ });
});
- test('changed handler for property bound to notifying computed property', function() {
+ test('changed handler for property bound to notifying computed property', function(done) {
var called = false;
el.boundcomputednotifyingvalueChanged = function() {
called = true;
};
el.$.basic1.notifyingvalue = 45;
- assert.equal(called, true, 'changed handler for property bound to non-notifying computed property not called');
+ setTimeout(function() {
+ assert.equal(called, true, 'changed handler for property bound to non-notifying computed property not called');
+ done();
+ });
});
test('no change for binding into read-only property', function() {
@@ -274,19 +315,25 @@ suite('1-way binding effects between elements', function() {
assert.equal(called, false, 'changed handler for property one-way-bound to non-notifying property called and should not have been');
});
- test('one-way binding to non-notifying computed property', function() {
+ test('one-way binding to non-notifying computed property', function(done) {
el.boundcomputedvalue = 42;
el.$.basic2.value = 43;
- assert.equal(el.boundcomputedvalue, 42, 'binding to non-notifying computed property updated and should not have been');
+ setTimeout(function() {
+ assert.equal(el.boundcomputedvalue, 42, 'binding to non-notifying computed property updated and should not have been');
+ done();
+ });
});
- test('changed handler for property one-way-bound to non-notifying computed property', function() {
+ test('changed handler for property one-way-bound to non-notifying computed property', function(done) {
var called = false;
el.boundcomputedvalueChanged = function() {
called = true;
};
el.$.basic2.value = 44;
- assert.equal(called, false, 'changed handler for property bound to non-notifying computed property called and should not have been');
+ setTimeout(function() {
+ assert.equal(called, false, 'changed handler for property bound to non-notifying computed property called and should not have been');
+ done();
+ });
});
test('one-way binding to notifying property', function() {
@@ -305,19 +352,25 @@ suite('1-way binding effects between elements', function() {
assert.equal(called, false, 'changed handler for property bound to notifying property called and should not have been');
});
- test('one-way binding to notifying computed property', function() {
+ test('one-way binding to notifying computed property', function(done) {
el.boundcomputednotifyingvalue = 42;
el.$.basic2.notifyingvalue = 43;
- assert.equal(el.boundcomputednotifyingvalue, 42, 'binding to notifying computed property updated and should not have been');
+ setTimeout(function() {
+ assert.equal(el.boundcomputednotifyingvalue, 42, 'binding to notifying computed property updated and should not have been');
+ done();
+ });
});
- test('changed handler for property one-way-bound to notifying computed property', function() {
+ test('changed handler for property one-way-bound to notifying computed property', function(done) {
var called = false;
el.boundcomputednotifyingvalueChanged = function() {
called = true;
};
el.$.basic2.notifyingvalue = 45;
- assert.equal(called, false, 'changed handler for property bound to non-notifying computed property called and should not have been');
+ setTimeout(function() {
+ assert.equal(called, false, 'changed handler for property bound to non-notifying computed property called and should not have been');
+ done();
+ });
});
});