5
<title>Example: Attribute Change Events</title>
6
<link rel="stylesheet" href="http://fonts.googleapis.com/css?family=Maven+Pro:400,700">
7
<link rel="stylesheet" href="../../build/cssgrids/grids-min.css">
8
<link rel="stylesheet" href="../assets/css/main.css">
9
<link rel="stylesheet" href="../assets/vendor/prettify/prettify-min.css">
10
<script src="../../build/yui/yui-min.js"></script>
15
<h1>Example: Attribute Change Events</h1>
20
<div class="yui3-u-3-4">
22
<div class="content"><style type="text/css" scoped>
24
padding:2px 2px 2px 5px;
27
#example-out .event-props {
32
#example-out .event-title {
42
border:1px solid #000;
44
background-color:#004c6d;
50
border:1px solid #000;
51
background-color:#cdcdcd;
57
background-color:#aaa;
77
.attrs #preventFoobar.hidden {
81
.attrs #preventFoobar {
90
<p>Attribute change events are one of the key benefits of using attributes to maintain state for your objects, instead of regular object properties. This example shows how you can listen for attribute change events and work with the event payload they receive.</p>
94
<form id="changeValue" class="attrs" action="#">
95
<div class="header">Enter a new value and click the "Change Value" button:</div>
98
<label for="attrSel">Attribute</label>:
100
<option value="foo">foo</option>
101
<option value="bar">bar</option>
102
<option value="foobar">foobar</option>
104
<label id="preventFoobar" class="hidden"><input type="checkbox" checked="true"> Prevent change</label>
106
<p><label for="currentVal">Current Value</label>: <span id="currentVal"></span></p>
107
<p><label for="newVal">New Value</label>: <input type="text" id="newVal" /></p>
110
<button type="submit">Change Value</button>
114
<div id="example-out"></div>
116
<script type="text/javascript">
117
// Get a new YUI instance
118
YUI().use("node", "attribute", "escape", function(Y) {
120
// Setup a custom class with attribute support
121
function MyClass(cfg) {
123
// Setup attribute configuration
138
this.addAttrs(attrs, cfg);
141
Y.augment(MyClass, Y.Attribute);
143
var o1 = new MyClass();
145
function displayEvent(e, title) {
146
var str = '<div class="event"><div class="event-title">' + title + '</div>';
150
'<ul class="event-props"><li>e.attrName: '
152
+ '</li><li>e.prevVal: '
153
+ Y.Escape.html(e.prevVal + "")
154
+ '</li><li>e.newVal: '
155
+ Y.Escape.html(e.newVal + "")
156
+ '</li></ul></div>';
161
Y.one("#example-out").prepend(str);
164
// Start Example Form Handling
165
var attrSel = Y.one("#attrSel");
166
var newValTxt = Y.one("#newVal");
167
var currentValSpan = Y.one("#currentVal");
168
var preventFoobarChk = Y.one("#preventFoobar input[type=checkbox]");
169
var preventFoobarLbl = Y.one("#preventFoobar");
171
var attrOpts = attrSel.get("options");
173
function updateVal(e) {
176
var selIndex = attrSel.get("selectedIndex");
177
var attr = attrOpts.item(selIndex).get("value");
178
o1.set(attr, newValTxt.get("value"));
181
Y.on("submit", updateVal, "#changeValue");
183
function populateCurrentValue() {
184
var selIndex = attrSel.get("selectedIndex");
185
var attr = attrOpts.item(selIndex).get("value");
187
currentValSpan.set("innerHTML", Y.Escape.html(o1.get(attr) + ""));
188
newValTxt.set("value", "");
190
if (attr === "foobar") {
191
preventFoobarLbl.removeClass("hidden");
193
preventFoobarLbl.addClass("hidden");
197
populateCurrentValue();
199
Y.on("change", populateCurrentValue, attrSel);
200
// End Example Form Handling
202
// Attribute Change Event Listners
204
o1.after("fooChange", function(e) {
205
displayEvent(e, "After fooChange");
206
currentValSpan.set("innerHTML", Y.Escape.html(e.newVal+""));
209
o1.after("barChange", function(e) {
210
displayEvent(e, "After barChange");
211
currentValSpan.set("innerHTML", Y.Escape.html(e.newVal+""));
214
o1.on("foobarChange", function(e) {
216
if (preventFoobarChk.get("checked")) {
218
// Calling preventDefault, in an "on" listener
219
// will prevent the attribute change from occuring
220
// and the after listener being called.
223
displayEvent(null, "On foobarChange (prevented)");
228
o1.after("foobarChange", function(e) {
230
// This foobar after listener will not get called,
231
// if we end up preventing default in the "on"
234
displayEvent(e, "After foobarChange");
235
currentValSpan.set("innerHTML", Y.Escape.html(e.newVal+""));
243
<h2>Listening For Attribute Change Events</h2>
245
<p>In this example, we'll look at how you can setup listeners for attribute change events, and work with the event payload which the listeners receive.</p>
247
<h3>Setting Up A Custom Class With Attribute</h3>
249
<p>We start by setting up the same custom class we created for the <a href="attribute-basic.html">basic example</a> with 3 attributes <code>foo</code>, <code>bar</code> and <code>foobar</code>, using the code below:</p>
251
<pre class="code prettyprint">YUI().use("attribute", "node", function(Y) {
253
// Setup a custom class with attribute support
254
function MyClass(cfg) {
256
// Setup attribute configuration
263
value:"Hello World!"
266
"foobar" : {
271
this.addAttrs(attrs, cfg);
274
Y.augment(MyClass, Y.Attribute);
279
<h3>Registering Event Listeners</h3>
281
<p>Once we have an instance of the custom class, we can use the <code>on</code> and <code>after</code> methods provided by Attribute, to listen for changes in the value of each of the attributes:</p>
283
<pre class="code prettyprint">var o1 = new MyClass();
287
// Event Listners
288
o1.after("fooChange", function(e) {
289
displayEvent(e, "After fooChange");
290
currentValSpan.set("innerHTML", Y.Escape.html(e.newVal+""));
293
o1.after("barChange", function(e) {
294
displayEvent(e, "After barChange");
295
currentValSpan.set("innerHTML", Y.Escape.html(e.newVal+""));
298
o1.on("foobarChange", function(e) {
300
if (preventFoobarChk.get("checked")) {
302
// Calling preventDefault, in an "on" listener
303
// will prevent the attribute change from occuring
304
// and the after listener being called.
307
displayEvent(null, "On foobarChange (prevented)");
312
o1.after("foobarChange", function(e) {
314
// This foobar after listener will not get called,
315
// if we end up preventing default in the "on"
316
// listener above.
318
displayEvent(e, "After foobarChange");
319
currentValSpan.set("innerHTML", Y.Escape.html(e.newVal+""));
323
<p>As seen in the above code, the event type for attribute change events is created by concatenating the attribute name with <code>"Change"</code> (e.g. <code>"fooChange"</code>), and this event type is used for both the <code>on</code> and <code>after</code> subscription methods. Whenever an attribute value is changed through Attribute's <code>set</code> method, both "on" and "after" subscribers are notified.</p>
325
<h3>On vs. After</h3>
327
<p><strong>on :</strong> Subscribers to the "on" moment, will be notified <em>before</em> any actual state change has occurred. This provides the opportunity to prevent the state change from occurring, using the <code>preventDefault</code> method of the event facade object passed to the subscriber. If you use <code>get</code> to retrieve the value of the attribute in an "on" subscriber, you will receive the current, unchanged value. However the event facade provides access to the value which the attribute is being set to, through it's <code>newVal</code> property.</p>
329
<p><strong>after :</strong> Subscribers to the "after" moment, will be notified <em>after</em> the attribute's state has been updated. This provides the opportunity to update state in other parts of your application, in response to a change in the attribute's state.</p>
331
<p>Based on the definition above, <code>after</code> listeners are not invoked if state change is prevented, for example, due to one of the <code>on</code> listeners calling <code>preventDefault</code> on the event object, as is done in the <code>on</code> listener for the <code>foobar</code> attribute:</p>
333
<pre class="code prettyprint">o1.on("foobarChange", function(event) {
335
// Calling preventDefault, in an "on" listener
336
// will prevent the attribute change from occurring
337
// and prevent the after listeners from being called
338
displayEvent(event, "on foobarChange (change prevented)");
340
event.preventDefault();
344
<p>For primitive values (non-Object values), the <code>after</code> listeners will also not be invoked if there is no change in the actual value of the attribute. That is, if the new value of the attribute is the same as the current value (based on the identity operator, <code>===</code>), the <code>after</code> listeners will not be notified because there is no change in state. You can see this, by setting an attribute to the same value twice in a row.</p>
346
<h3>Event Facade</h3>
348
<p>The event object (an instance of <a href="http://yuilibrary.com/yui/docs/api/EventFacade.html">EventFacade</a>) passed to attribute change event subscribers, has the following interesting properties and methods related to attribute management:</p>
352
<dd>The value which the attribute will be set to (in the case of "on" subscribers), or has been set to (in the case of "after" subscribers</dd>
354
<dd>The value which the attribute is currently set to (in the case of "on" subscribers), or was previously set to (in the case of "after" subscribers</dd>
356
<dd>The name of the attribute which is being set</dd>
358
<dd>Attribute also allows you to set nested properties of attributes which have values which are objects through the
359
<code>set</code> method (e.g. <code>o1.set("x.y.z")</code>). This property will contain the path to the property which was changed.</dd>
360
<dt>preventDefault()<dt>
361
<dd>This method can be called in an "on" subscriber to prevent the attribute's value from being updated (the default behavior). Calling this method in an "after" listener has no impact, since the default behavior has already been invoked.</dd>
362
<dt>stopImmediatePropagation()</dt>
363
<dd>This method can be called in "on" or "after" subscribers, and will prevent the rest of the subscriber stack from
364
being invoked, but will not prevent the attribute's value from being updated.</dd>
367
<p>The <a href="attribute-event-speeddate.html">"Attribute Event Based Speed Dating" example</a> provides a look at how you can leverage attribute change events in your applications, to decouple logic both within your class, and when interacting with other objects.</p>
369
<h2>Complete Example Source</h2>
371
<pre class="code prettyprint"><form id="changeValue" class="attrs" action="#">
372
<div class="header">Enter a new value and click the "Change Value" button:</div>
373
<div class="body">
375
<label for="attrSel">Attribute</label>:
376
<select id="attrSel">
377
<option value="foo">foo</option>
378
<option value="bar">bar</option>
379
<option value="foobar">foobar</option>
381
<label id="preventFoobar" class="hidden"><input type="checkbox" checked="true"> Prevent change</label>
383
<p><label for="currentVal">Current Value</label>: <span id="currentVal"></span></p>
384
<p><label for="newVal">New Value</label>: <input type="text" id="newVal" /></p>
386
<div class="footer">
387
<button type="submit">Change Value</button>
391
<div id="example-out"></div>
393
<script type="text/javascript">
394
// Get a new YUI instance
395
YUI().use("node", "attribute", "escape", function(Y) {
397
// Setup a custom class with attribute support
398
function MyClass(cfg) {
400
// Setup attribute configuration
407
value:"Hello World!"
410
"foobar" : {
415
this.addAttrs(attrs, cfg);
418
Y.augment(MyClass, Y.Attribute);
420
var o1 = new MyClass();
422
function displayEvent(e, title) {
423
var str = '<div class="event"><div class="event-title">' + title + '</div>';
427
'<ul class="event-props"><li>e.attrName: '
429
+ '</li><li>e.prevVal: '
430
+ Y.Escape.html(e.prevVal + "")
431
+ '</li><li>e.newVal: '
432
+ Y.Escape.html(e.newVal + "")
433
+ '</li></ul></div>';
436
str += '</div>';
438
Y.one("#example-out").prepend(str);
441
// Start Example Form Handling
442
var attrSel = Y.one("#attrSel");
443
var newValTxt = Y.one("#newVal");
444
var currentValSpan = Y.one("#currentVal");
445
var preventFoobarChk = Y.one("#preventFoobar input[type=checkbox]");
446
var preventFoobarLbl = Y.one("#preventFoobar");
448
var attrOpts = attrSel.get("options");
450
function updateVal(e) {
453
var selIndex = attrSel.get("selectedIndex");
454
var attr = attrOpts.item(selIndex).get("value");
455
o1.set(attr, newValTxt.get("value"));
458
Y.on("submit", updateVal, "#changeValue");
460
function populateCurrentValue() {
461
var selIndex = attrSel.get("selectedIndex");
462
var attr = attrOpts.item(selIndex).get("value");
464
currentValSpan.set("innerHTML", Y.Escape.html(o1.get(attr) + ""));
465
newValTxt.set("value", "");
467
if (attr === "foobar") {
468
preventFoobarLbl.removeClass("hidden");
470
preventFoobarLbl.addClass("hidden");
474
populateCurrentValue();
476
Y.on("change", populateCurrentValue, attrSel);
477
// End Example Form Handling
479
// Attribute Change Event Listners
481
o1.after("fooChange", function(e) {
482
displayEvent(e, "After fooChange");
483
currentValSpan.set("innerHTML", Y.Escape.html(e.newVal+""));
486
o1.after("barChange", function(e) {
487
displayEvent(e, "After barChange");
488
currentValSpan.set("innerHTML", Y.Escape.html(e.newVal+""));
491
o1.on("foobarChange", function(e) {
493
if (preventFoobarChk.get("checked")) {
495
// Calling preventDefault, in an "on" listener
496
// will prevent the attribute change from occuring
497
// and the after listener being called.
500
displayEvent(null, "On foobarChange (prevented)");
505
o1.after("foobarChange", function(e) {
507
// This foobar after listener will not get called,
508
// if we end up preventing default in the "on"
509
// listener above.
511
displayEvent(e, "After foobarChange");
512
currentValSpan.set("innerHTML", Y.Escape.html(e.newVal+""));
516
</script></pre>
522
<div class="yui3-u-1-4">
523
<div class="sidebar">
527
<div class="sidebox">
529
<h2 class="no-toc">Examples</h2>
533
<ul class="examples">
536
<li data-description="Use the Attribute API to define, set and get attribute values.">
537
<a href="attribute-basic.html">Basic Attribute Configuration</a>
542
<li data-description="Configure attributes to be readOnly or writeOnce.">
543
<a href="attribute-rw.html">Read-Only and Write-Once Attributes</a>
548
<li data-description="How to listen for changes in attribute values.">
549
<a href="attribute-event.html">Attribute Change Events</a>
554
<li data-description="Create a basic SpeedDater class, with Attribute support.">
555
<a href="attribute-basic-speeddate.html">Attribute Based Speed Dating</a>
560
<li data-description="Refactors the basic Speed Dating example, to use attribute change events to update rendered elements, and have two instances react to another.">
561
<a href="attribute-event-speeddate.html">Attribute Event Based Speed Dating</a>
566
<li data-description="Add custom methods to get and set attribute values and provide validation support.">
567
<a href="attribute-getset.html">Attribute Getters, Setters and Validators</a>
582
<script src="../assets/vendor/prettify/prettify-min.js"></script>
583
<script>prettyPrint();</script>