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); + }); +}); +