Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

0.8 custom notify event #1335

Merged
merged 5 commits into from
Apr 1, 2015
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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