diff --git a/src/lib/template/templatizer.html b/src/lib/template/templatizer.html
index 0b6cf5d397..5c0daa1696 100644
--- a/src/lib/template/templatizer.html
+++ b/src/lib/template/templatizer.html
@@ -282,12 +282,14 @@
_createHostPropEffector: function(prop) {
var prefix = this._parentPropPrefix;
return function(source, value) {
+ if (source !== prop) return;
this.dataHost._templatized[prefix + prop] = value;
};
},
_createInstancePropEffector: function(prop) {
return function(source, value, old, fromAbove) {
+ if (source !== prop) return;
if (!fromAbove) {
this.dataHost._forwardInstanceProp(this, prop, value);
}
diff --git a/src/standard/effectBuilder.html b/src/standard/effectBuilder.html
index 89e064b266..09b79259a1 100644
--- a/src/standard/effectBuilder.html
+++ b/src/standard/effectBuilder.html
@@ -35,6 +35,33 @@
var prop = Polymer.Bind.addPropertyEffect(this, property, kind, effect);
// memoize path function for faster lookup.
prop.pathFn = this['_' + prop.kind + 'PathEffect'];
+ return prop;
+ },
+
+ addCustomEffect: function(property, fn) {
+ // In case this is the first effect of the property, we MUST create the
+ // underlying trigger machinery by hand.
+ var effects = Polymer.Bind.ensurePropertyEffects(this, property);
+ if (effects.length === 0) {
+ // The current value will be masked by the descriptor, read it ...
+ var val = this[property];
+ Polymer.Bind._createAccessors(this, property, effects);
+ // ... and apply on our data store
+ this.__data__[property] = val;
+ }
+ // Theoretically the effects must be sorted again, but `function`-effects
+ // are executed last, so we can skip this for now.
+ return this._addPropertyEffect(property, 'function', fn);
+ },
+
+ removeCustomEffect: function(property, fx) {
+ var effects = this._propertyEffects && this._propertyEffects[property];
+ if (effects) {
+ var index = effects.indexOf(fx);
+ if (index !== -1) {
+ effects.splice(index, 1);
+ }
+ }
},
// prototyping
diff --git a/src/standard/notify-path.html b/src/standard/notify-path.html
index 0174974c44..ab3f6dac04 100644
--- a/src/standard/notify-path.html
+++ b/src/standard/notify-path.html
@@ -323,6 +323,10 @@
}
},
+ _functionPathEffect: function(path, value, effect) {
+ Polymer.Bind._functionEffect.call(this, path, value, effect);
+ },
+
_pathMatchesEffect: function(path, effect) {
var effectArg = effect.trigger.name;
return (effectArg == path) ||
diff --git a/test/unit/bind-elements.html b/test/unit/bind-elements.html
index 665df9fd8e..a4c32cf32e 100644
--- a/test/unit/bind-elements.html
+++ b/test/unit/bind-elements.html
@@ -662,3 +662,7 @@
});
+
+
\ No newline at end of file
diff --git a/test/unit/bind.html b/test/unit/bind.html
index a2208a3623..84fa54474f 100644
--- a/test/unit/bind.html
+++ b/test/unit/bind.html
@@ -1000,6 +1000,81 @@
});
});
+suite('custom user effects', function() {
+
+ test('Add custom effect', function() {
+ var el = document.createElement('x-custom-effect');
+
+ var called = 0;
+ el.addCustomEffect('foo', function(path, value, old) {
+ called += 1;
+ assert.equal(path, 'foo');
+ assert.equal(value, 'bar');
+ assert.equal(old, undefined);
+ });
+
+ el.foo = 'bar';
+ assert.equal(called, 1);
+ });
+
+ test('Remove custom effect', function() {
+ var el = document.createElement('x-custom-effect');
+
+ var called = 0;
+ var fx = el.addCustomEffect('foo', function() {
+ called += 1;
+ });
+
+ el.removeCustomEffect('foo', fx);
+
+ el.foo = 'bar';
+ assert.equal(called, 0);
+ });
+
+ test('Ensure old values are sent', function() {
+ var el = document.createElement('x-custom-effect');
+ el.foo = 'bar';
+
+ var called = 0;
+ el.addCustomEffect('foo', function(path, value, old) {
+ called += 1;
+ assert.equal(old, 'bar');
+ });
+
+ el.foo = 'quux';
+ assert.equal(called, 1);
+ });
+
+ test('Ensure path effects can be seen', function() {
+ var el = document.createElement('x-custom-effect');
+ el.foo = {bar: 'quux'};
+
+ var called = 0;
+ el.addCustomEffect('foo', function(path, value, old) {
+ called += 1;
+ assert.equal(path, 'foo.bar');
+ assert.equal(value, 'quod');
+ assert.equal(old, undefined); // always undefined for structured paths!
+ });
+
+ el.set('foo.bar', 'quod');
+ assert.equal(called, 1);
+ });
+
+ test('Ensure independence', function() {
+ var el1 = document.createElement('x-custom-effect');
+ var called = 0;
+ el1.addCustomEffect('foo', function() {
+ called += 1;
+ });
+
+ var el2 = document.createElement('x-custom-effect');
+
+ el2.foo = 'bar';
+ assert.equal(called, 0);
+ });
+});
+