Skip to content

Commit

Permalink
Merge pull request #1335 from Polymer/0.8-custom-notify-event
Browse files Browse the repository at this point in the history
0.8 custom notify event
  • Loading branch information
Steve Orvell committed Apr 1, 2015
2 parents 46cde43 + 835253c commit a8f8013
Show file tree
Hide file tree
Showing 7 changed files with 230 additions and 28 deletions.
14 changes: 5 additions & 9 deletions src/features/standard/configure.html
Original file line number Diff line number Diff line change
Expand Up @@ -139,14 +139,10 @@
// effects and side effects must not be processed before ready time,
// handling is queue/defered until then.
_notifyListener: function(fn, e) {
// TODO(kschaaf): This target check really belongs in the generated
// handler, but e.target is no longer valid after queueing
if (!e.path || e.path[0] == e.target) {
if (!this._readied) {
this._queueHandler(arguments);
} else {
return fn.call(this, e);
}
if (!this._readied) {
this._queueHandler([fn, e, e.target]);
} else {
return fn.call(this, e, e.target);
}
},

Expand All @@ -157,7 +153,7 @@
_flushHandlers: function() {
var h$ = this._handlers;
for (var i=0, l=h$.length, h; (i<l) && (h=h$[i]); i++) {
h[0].call(this, h[1]);
h[0].call(this, h[1], h[2]);
}
}

Expand Down
9 changes: 8 additions & 1 deletion src/lib/annotations/annotations.html
Original file line number Diff line number Diff line change
Expand Up @@ -214,6 +214,12 @@
name = n.slice(0, -1);
kind = 'attribute';
}
// Custom notification event
var notifyEvent, colon;
if (mode == '{' && (colon = v.indexOf('::')) > 0) {
notifyEvent = v.substring(colon + 2);
v = v.substring(0, colon);
}
// Remove annotation
node.removeAttribute(n);
// Case hackery: attributes are lower-case, but bind targets
Expand All @@ -228,7 +234,8 @@
mode: mode,
name: name,
value: v,
negate: not
negate: not,
event: notifyEvent
};
}
},
Expand Down
5 changes: 2 additions & 3 deletions src/lib/bind/bind-effects.html
Original file line number Diff line number Diff line change
Expand Up @@ -109,10 +109,9 @@
annotation: function(model, hostProperty, info) {
var property = info.name;
if (Polymer.Bind._shouldAddListener(info)) {
var dashCaseProperty = Polymer.CaseMap.camelToDashCase(property);
// <node>.on.<dash-case-property>-changed: <path> = e.detail.value
Polymer.Bind._addAnnotatedListener(model, info.index,
dashCaseProperty, info.value);
Polymer.Bind._addAnnotatedListener(model, info.index,
property, info.value, info.event);
}
//
if (!property) {
Expand Down
25 changes: 14 additions & 11 deletions src/lib/bind/bind.html
Original file line number Diff line number Diff line change
Expand Up @@ -20,32 +20,35 @@
model._clearPath = this._clearPath;
},

_addAnnotatedListener: function(model, index, property, path) {
_addAnnotatedListener: function(model, index, property, path, event) {
// <node>.on.<property>-changed: <path> = e.detail.value
//
var changedFn = '\t\tthis.' + path + ' = e.detail.value;';
var value = 'target.' + property;
var changedFn = '\t\tthis.' + path + ' = ' + value + ';';
if (path.indexOf('.') > 0) {
// TODO(kschaaf): dirty check avoids null references when the object has gone away
changedFn =
'\tif (this._data[\'' + path + '\'] != e.detail.value) {\n' +
'\tif (this._data[\'' + path + '\'] != ' + value + ') {\n' +
'\t\t' + changedFn + '\n' +
'\t\tthis.notifyPath(\'' + path + '\', e.detail.value)\n' +
'\t\tthis.notifyPath(\'' + path + '\', ' + value + ')\n' +
'\t}';
}
changedFn =
'if (e.detail.path) {\n' +
'\tvar path = this._fixPath(\'' + path + '\', \'' +
'if (!e.path || e.path[0] == target) {\n' +
'\tif (e.detail && e.detail.path) {\n' +
'\t\tvar path = this._fixPath(\'' + path + '\', \'' +
property + '\', e.detail.path);\n' +
'\tthis.notifyPath(path, e.detail.value);\n' +
'} else {\n' +
'\t\tthis.notifyPath(path, e.detail.value);\n' +
'\t} else {\n' +
changedFn + '\n' +
'\t}' +
'}';
//
model._bindListeners.push({
index: index,
property: property,
path: path,
changedFn: new Function('e', changedFn)
changedFn: new Function('e', 'target', changedFn),
event: event || (Polymer.CaseMap.camelToDashCase(property) + '-changed')
});
},

Expand Down Expand Up @@ -180,7 +183,7 @@
// <node>.on.<property>-changed: <path]> = e.detail.value
//console.log('[_setupBindListener]: [%s][%s] listening for [%s][%s-changed]', this.localName, info.path, info.id || info.index, info.property);
var node = inst._nodes[info.index];
node.addEventListener(info.property + '-changed', inst._notifyListener.bind(inst, info.changedFn));
node.addEventListener(info.event, inst._notifyListener.bind(inst, info.changedFn));
});
},

Expand Down
155 changes: 155 additions & 0 deletions test/smoke/custom-notify-smoke.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,155 @@
<!doctype html>
<!--
@license
Copyright (c) 2014 The Polymer Project Authors. All rights reserved.
This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
Code distributed by Google as part of the polymer project is also
subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
-->
<html>
<head>

<script src="../../../webcomponentsjs/webcomponents-lite.js"></script>

<title>Custom Notify Smoke Test</title>

<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">

<link rel="import" href="../../polymer.html">

<style>
body {
font-family: sans-serif;
font-size: 15px;
}
</style>

</head>
<body>

<dom-module id="x-app">
<style>
</style>
<template>

<h1> Works correctly </h1>

<h3>&lt;input value="{{inputValue::input}}"&gt;</h3>
<input value="{{inputValue::input}}">
<input value="{{inputValue::input}}">

<hr>

<h3>&lt;input value="{{inputValueCommitted::change}}&gt;</h3>
<input value="{{inputValueCommitted::change}}">
<input value="{{inputValueCommitted::change}}">

<hr>

<h3>&lt;value="{{textareaValue::input}}"&gt;</h3>
<textarea value="{{textareaValue::input}}"></textarea>
<textarea value="{{textareaValue::input}}"></textarea>

<hr>

<h3>&lt;textarea value="{{textareaValueCommitted::change}}"&gt;</h3>
<textarea value="{{textareaValueCommitted::change}}"></textarea>
<textarea value="{{textareaValueCommitted::change}}"></textarea>

<hr>

<h3>&lt;input type="checkbox"&gt;.checked</h3>
<input type="checkbox" checked="{{checked::change}}">
<input type="checkbox" checked="{{checked::change}}">

<hr>

<h3>&lt;input type="date" value="{{date::change}}"&gt;</h3>
<input type="date" value="{{date::change}}">
<input type="date" value="{{date::change}}">

<hr>

<h3>&lt;select selected-index="{{selectedIndex::change}}"&gt;</h3>
<select selected-index="{{selectedIndex::change}}">
<option>a</option>
<option>b</option>
<option>c</option>
</select>
<select selected-index="{{selectedIndex::change}}">
<option>a</option>
<option>b</option>
<option>c</option>
</select>

<hr>

<div>Current time: <span>{{currentTime}}</span></div>
<video current-time="{{currentTime::timeupdate}}" src="http://media.w3.org/2010/05/sintel/trailer.mp4" height="200" controls></video>

<hr>

<h1> Does not work as expected </h1>

<h3>&lt;select value="{{selectedValue::change}}"&gt;</h3>
<p><em>Setting `value` on IE inexplicably clears the value</em></p>
<select value="{{selectedValue::change}}">
<option>a</option>
<option>b</option>
<option>c</option>
</select>
<select value="{{selectedValue::change}}">
<option>a</option>
<option>b</option>
<option>c</option>
</select>

<hr>

<h3>&lt;input type="radio" name="one" checked="{{aChecked::change}}"&gt;</h3>
<p><em>Radio buttons do not fire changed events when unchecking</em></p>

<label><input type="radio" name="one" checked="{{aChecked::change}}">a</label>
<label><input type="radio" name="one" checked="{{bChecked::change}}">b</label>
<label><input type="radio" name="one" checked="{{cChecked::change}}">c</label>
<br><br>
<label><input type="radio" name="two" checked="{{aChecked::change}}">a</label>
<label><input type="radio" name="two" checked="{{bChecked::change}}">b</label>
<label><input type="radio" name="two" checked="{{cChecked::change}}">c</label>

<hr>

<h3>&lt;input type="range" value="{{rangeValue::input}}"&gt;</h3>
<p><em>IE does not fire <strong>input</strong> event for range input</em></p>

<input type="range" value="{{rangeValue::input}}">
<input type="range" value="{{rangeValue::input}}">
<hr>

<h3>&lt;input type="range" value="{{rangeValueCommitted::change}}"&gt;</h3>
<p><em>IE fires <strong>change</strong> event immediately when dragging; all others fire change when stopping drag</em></p>
<input type="range" value="{{rangeValueCommitted::change}}">
<input type="range" value="{{rangeValueCommitted::change}}">


</template>

</dom-module>

<script>

document.addEventListener('WebComponentsReady', function() {
Polymer({
is: 'x-app'
});
});

</script>

<x-app></x-app>

</body>
</html>
32 changes: 28 additions & 4 deletions test/unit/bind-elements.html
Original file line number Diff line number Diff line change
@@ -1,6 +1,19 @@
<template>
<div id="boundChild" value="{{value}}" negvalue="{{!bool}}" attrvalue$="{{attrvalue}}"
computedvalue="{{computedvalue}}" computedvaluetwo="{{computedvaluetwo}}" camel-case="{{value}}" computed-inline="{{computeInline(value,add, divide)}}" computed-inline2="{{computeInline(value, add,divide)}}" computedattribute$="{{computeInline(value, add,divide)}}" style$="{{boundStyle}}">Test</div>
<div id="boundChild"
value="{{value}}"
negvalue="{{!bool}}"
attrvalue$="{{attrvalue}}"
computedvalue="{{computedvalue}}"
computedvaluetwo="{{computedvaluetwo}}"
camel-case="{{value}}"
computed-inline="{{computeInline(value,add, divide)}}"
computed-inline2="{{computeInline(value, add,divide)}}"
computedattribute$="{{computeInline(value, add,divide)}}"
style$="{{boundStyle}}"
custom-event-value="{{customEventValue::custom}}"
custom-event-object-value="{{customEventObject.value::change}}">
Test
</div>
</template>
<script>
Polymer({
Expand Down Expand Up @@ -50,10 +63,19 @@
},
divide: {
value: 2
},
customEventValue: {
type: Number,
observer: 'customEventValueChanged'
},
customEventObject: {
type: Object,
value: function() { return {}; }
}
},
observers: {
'dep1 dep2 dep3': 'multipleDepChangeHandler'
'dep1 dep2 dep3': 'multipleDepChangeHandler',
'customEventObject.value': 'customEventObjectValueChanged'
},
valueChanged: function() {},
computeValue: function(val) {
Expand All @@ -73,7 +95,9 @@
multipleDepChangeHandler: function() {},
computeInline: function(value, add, divide) {
return (value + add) / divide;
}
},
customEventValueChanged: function() {},
customEventObjectValueChanged: function() {}
});
</script>

Expand Down
18 changes: 18 additions & 0 deletions test/unit/bind.html
Original file line number Diff line number Diff line change
Expand Up @@ -220,6 +220,24 @@
assert.equal(getComputedStyle(el.$.boundChild).paddingTop, '37px', 'style attribute binding not correct');
});

test('custom notification event to property', function() {
var called = 0;
el.customEventValueChanged = function() { called++; };
el.$.boundChild.customEventValue = 42;
el.fire('custom', null, el.$.boundChild);
assert.equal(el.customEventValue, 42, 'custom bound property incorrect');
assert.equal(called, 1, 'custom bound property observer not called');
});

test('custom notification event to path', function() {
var called = 0;
el.customEventObjectValueChanged = function() { called++; };
el.$.boundChild.customEventObjectValue = 84;
el.fire('change', null, el.$.boundChild);
assert.equal(el.customEventObject.value, 84, 'custom bound path incorrect');
assert.equal(called, 1, 'custom bound path observer not called');
});

});

</script>
Expand Down

0 comments on commit a8f8013

Please sign in to comment.