Skip to content
This repository has been archived by the owner on Mar 13, 2018. It is now read-only.

Commit

Permalink
allow computed properties to be bindable, fixes Polymer/polymer#638
Browse files Browse the repository at this point in the history
  • Loading branch information
John Messerly committed Aug 29, 2014
1 parent 0e207e2 commit 2d9e07e
Show file tree
Hide file tree
Showing 3 changed files with 73 additions and 23 deletions.
18 changes: 13 additions & 5 deletions src/declaration/properties.js
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,7 @@
}
return map;
},
createPropertyAccessor: function(name) {
createPropertyAccessor: function(name, ignoreWrites) {
var proto = this.prototype;

var privateName = name + '_';
Expand All @@ -145,6 +145,10 @@
return this[privateName];
},
set: function(value) {
if (ignoreWrites) {
return this[privateName];
}

var observable = this[privateObservable];
if (observable) {
observable.setValue(value);
Expand All @@ -161,16 +165,20 @@
});
},
createPropertyAccessors: function(prototype) {
var n$ = prototype._publishNames;
var n$ = prototype._computedNames;
if (n$ && n$.length) {
for (var i=0, l=n$.length, n, fn; (i<l) && (n=n$[i]); i++) {
this.createPropertyAccessor(n);
this.createPropertyAccessor(n, true);
}
}
var n$ = prototype._computedNames;
var n$ = prototype._publishNames;
if (n$ && n$.length) {
for (var i=0, l=n$.length, n, fn; (i<l) && (n=n$[i]); i++) {
this.createPropertyAccessor(n);
// If the property is computed and published, the accessor is created
// above.
if (!prototype.computed || !prototype.computed[n]) {
this.createPropertyAccessor(n);
}
}
}
}
Expand Down
31 changes: 27 additions & 4 deletions src/instance/properties.js
Original file line number Diff line number Diff line change
Expand Up @@ -148,15 +148,27 @@
bindToAccessor: function(name, observable, resolveFn) {
var privateName = name + '_';
var privateObservable = name + 'Observable_';
// Present for properties which are computed and published and have a
// bound value.
var privateComputedBoundValue = name + 'ComputedBoundObservable_';

this[privateObservable] = observable;

var oldValue = this[privateName];

var self = this;
var value = observable.open(function(value, oldValue) {
function updateValue(value, oldValue) {
self[privateName] = value;

var setObserveable = self[privateComputedBoundValue];
if (setObserveable && typeof setObserveable.setValue == 'function') {
setObserveable.setValue(value);
}

self.emitPropertyChangeRecord(name, value, oldValue);
});
}

var value = observable.open(updateValue);

if (resolveFn && !areSameValue(oldValue, value)) {
var resolvedValue = resolveFn(oldValue, value);
Expand All @@ -167,13 +179,13 @@
}
}

this[privateName] = value;
this.emitPropertyChangeRecord(name, value, oldValue);
updateValue(value, oldValue);

var observer = {
close: function() {
observable.close();
self[privateObservable] = undefined;
self[privateComputedBoundValue] = undefined;
}
};
this.registerObserver(observer);
Expand Down Expand Up @@ -201,6 +213,17 @@
this[property] = observable;
return;
}
var computed = this.element.prototype.computed;

// Binding an "out-only" value to a computed property. Note that
// since this observer isn't opened, it doesn't need to be closed on
// cleanup.
if (computed && computed[property]) {
var privateComputedBoundValue = property + 'ComputedBoundObservable_';
this[privateComputedBoundValue] = observable;
return;
}

return this.bindToAccessor(property, observable, resolveBindingValue);
},
invokeMethod: function(method, args) {
Expand Down
47 changes: 33 additions & 14 deletions test/html/computedProperties.html
Original file line number Diff line number Diff line change
Expand Up @@ -16,29 +16,48 @@
<script src="../../../tools/test/chai/chai.js"></script>
</head>
<body>
<x-foo foo="mee" bar="too" count=3></x-foo>

<polymer-element name="x-foo" attributes="foo bar count">
<template>{{ fooBar }}:{{ fooBarCounted }}</template>
<polymer-element name="x-foo" attributes="a b concat upper">
<template>{{ a }}:{{ b }}:{{ concat }}:{{ upper }}</template>
<script>
Polymer('x-foo', {
computed: {
fooBarCounted: 'repeat(fooBar, count)',
fooBar: "foo + '-' + bar"
},
repeat: function(str, count) {
var retval = '';
for (var i = 0; i < count; i++) {
retval += (i ? ' ' : '') + str + '(' + i + ')';
}
return retval;
concat: "a + b",
upper: "toUpperCase(concat)",
},
toUpperCase: function(str) {
return str.toUpperCase();
}
})
</script>
</polymer-element>

<polymer-element name="computed-properties-test">
<template>
<x-foo id="foo" a="a" b="b" concat="{{ concat }}" upper="{{ upper }}"></x-foo>
</template>
<script>
Polymer('computed-properties-test', {
concat: null,
upper: null,
ready: function() {
chai.assert.equal(this.shadowRoot.innerHTML, 'mee-too:mee-too(0) mee-too(1) mee-too(2)');
var foo = this.$.foo;
chai.assert.equal(foo.shadowRoot.innerHTML, 'a:b:ab:AB');
chai.assert.equal(this.concat, 'ab');
chai.assert.equal(this.upper, 'AB');
chai.assert.equal(foo.concat, 'ab');
chai.assert.equal(foo.upper, 'AB');
foo.concat = 'bogus';
foo.upper = 'bogus';
chai.assert.equal(this.concat, 'ab');
chai.assert.equal(this.upper, 'AB');
chai.assert.equal(foo.concat, 'ab');
chai.assert.equal(foo.upper, 'AB');
done();
}
})
</script>
</polymer-element>

<computed-properties-test></computed-properties-test>
</body>
</html>

0 comments on commit 2d9e07e

Please sign in to comment.