Cross Reference: /yui3/src/attribute/docs/attribute-event-speeddate.mustache
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai<style type="text/css" scoped>
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai #speeddate h1 {
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai font-size: 108%;
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai color:#000;
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai margin-bottom:2em;
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai }
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai #john {
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai margin-bottom:10px;
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai }
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai .interests.disabled, .reconsider.disabled {
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai color:#888;
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai }
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai #john .interest {
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai margin-left:5px;
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai }
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai .sd-nametag {
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai border:1px solid #000;
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai text-align:center;
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai width:25em;
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai margin:20px;
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai background-color:#00f;
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai border-radius: 10px;
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai -webkit-border-radius: 10px;
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai -moz-border-radius: 10px;
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai box-shadow: 3px 3px 3px #888;
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai -moz-box-shadow: 3px 3px 3px #888;
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai -webkit-box-shadow: 3px 3px 3px #888;
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai }
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai .sd-nametag .sd-hd,
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai .sd-nametag .sd-ft {
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai padding:5px;
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai text-align:center;
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai font-size:108%;
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai font-weight:900;
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai color:#fff;
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai }
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai .sd-nametag .sd-hd {
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai border-top-right-radius: 10px;
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai border-top-left-radius: 10px;
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai -moz-border-radius-topright: 10px;
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai -moz-border-radius-topleft: 10px;
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai -webkit-border-top-right-radius: 10px;
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai -webkit-border-top-left-radius: 10px;
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai }
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai .sd-nametag .sd-ft {
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai border-bottom-right-radius: 10px;
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai border-bottom-left-radius: 10px;
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai -moz-border-radius-bottomright: 10px;
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai -moz-border-radius-bottomleft: 10px;
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai -webkit-border-bottom-right-radius: 10px;
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai -webkit-border-bottom-left-radius: 10px;
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai }
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai .sd-nametag .sd-bd {
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai background-color:#fff;
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai padding:0.5em;
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai }
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai .sd-nametag .sd-bd .sd-name,
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai .sd-nametag .sd-bd .sd-personality,
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai .sd-nametag .sd-bd .sd-interests {
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai font-size:108%;
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai font-weight:900;
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai font-family:monospace;
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai text-decoration:underline;
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai color:#00a;
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai }
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai</style>
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai<div class="intro">
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai <p>Attribute change events are one of the key benefits of using attributes to maintain state for your objects, instead of regular object properties.</p>
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai <p>This example refactors the basic <a href="attribute-basic-speeddate.html">"Attribute Based Speed Dating" example</a> to shows how you can listen for attribute change events to tie together your object's internal logic (such as updating the visual presentation of the object), and also to communicate with other objects.</p>
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai</div>
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai<div class="example">
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai {{>attribute-event-speeddate-source}}
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai</div>
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai<h2>Listening For Attribute Change Events</h2>
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai<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,
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desaiusing the `SpeedDater` class, introduced in the <a href="attribute-basic-speeddate.html">"Attribute Based Speed Dating" example</a>.</p>
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai<p>We'll create two SpeedDater instances, `jane` and `john`, and use the attribute events they generate both internally (within the class code), to wire up UI refreshes,
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desaiand externally, to have `jane` react to changes in the `john`'s state.</p>
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai<h3>Setting Up The SpeedDater Class With Attribute</h3>
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai<p>We start by setting up the same basic class we created for the <a href="attribute-basic-speeddate.html">"Attribute Based Speed Dating" example</a>, with an additional attribute, `interests`, using the code below:</p>
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai```
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai// Setup custom class which we want to add managed attribute support to
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desaifunction SpeedDater(cfg) {
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai // When constructed, setup the initial attributes for the instance,
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai // by calling the addAttrs method.
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai var attrs = {
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai name : {
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai writeOnce:true
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai },
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai personality : {
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai value:50
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai },
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai available : {
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai value:true
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai },
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai interests : {
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai value : []
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai }
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai };
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai this.addAttrs(attrs, cfg);
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai}
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai// Augment custom class with Attribute
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen DesaiY.augment(SpeedDater, Y.Attribute);
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai```
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai<p>We then create two instances of SpeedDaters, `jane` and `john`:</p>
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai```
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai// Create a john instance...
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desaijohn = new SpeedDater({
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai name: "John",
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai personality: 78
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai});
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai// ... and render to the page
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desaijohn.applyNameTag("#john .shirt");
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai// Create a jane instance...
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desaijane = new SpeedDater({
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai name: "Jane",
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai personality: 82,
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai interests: ["Popcorn", "Saving Whales"]
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai});
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desaijane.applyNameTag("#jane .shirt");
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai```
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai<h3>Registering Event Listeners</h3>
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai<p>For this event based example, we no longer have an `updateNameTag()` method which the user is responsible for calling when they want to refresh the name tag rendered on the page, as we did in the basic example.
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen DesaiInstead the `SpeedDater` class sets up some internal attribute change event listeners in its `listenForChanges()` method, which will refresh the UI for a particular attribute, each time its value is modified:</p>
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai```
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai// Method used to attach attribute change event listeners, so that
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai// the SpeedDater instance will react to changes in attribute state,
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai// and update what's rendered on the page
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen DesaiSpeedDater.prototype.listenForChanges = function() {
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai // Sync up the UI for "available", after the value of the "available"
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai // attribute has changed:
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai this.after("availableChange", function(e) {
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai var taken = (e.newVal) ? "" : "Oh, is that the time?";
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai this.nameTag.one(".sd-availability").set("innerHTML", taken);
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai });
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai // Sync up the UI for "name", after the value of the "name"
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai // attribute has changed:
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai this.after("nameChange", function(e) {
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai var name = e.newVal;
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai this.nameTag.one(".sd-name").set("innerHTML", name);
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai });
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai // Sync up the UI for "personality", after the value of the "personality"
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai // attribute has changed:
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai this.after("personalityChange", function(e) {
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai var personality = e.newVal;
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai var personalityEl = this.nameTag.one(".sd-personality");
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai personalityEl.set("innerHTML", personality);
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai if (personality > 90) {
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai personalityEl.addClass("sd-max");
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai }
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai });
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai // Sync up the UI for "interests", after the value of the "interests"
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai // attribute has changed:
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai this.after("interestsChange", function(e) {
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai var interests = (e.newVal.length == 0) ?
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai "absolutely nothing" : this.get("interests").join(", ");
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai this.nameTag.one(".sd-interests").set("innerHTML", interests);
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai });
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai};
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai```
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai<p>
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen DesaiAs seen in the above code, the event type for attribute change events is created by concatenating the attribute name with `"Change"` (e.g. `"availableChange"`). Whenever an attribute value is changed through Attribute's `set()` method, both <em>"on"</em> and <em>"after"</em> subscribers are notified.
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai</p>
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai<p>
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen DesaiIn the code snippet above, all the subscribers are listening for the <em>"after"</em> moment using the `after()` subscription method, since they're only interested in being notified after the value has actually changed.
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen DesaiHowever, as we'll see below, the example also shows you how to use an <em>"on"</em> listener, to prevent the attribute state change from occuring under certain conditions.
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai</p>
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai<h3>On vs. After</h3>
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai<p>A single attribute change event has two moments which can be subscribed to, depending on what the subscriber wants to do when notified.</p>
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai<p><strong>on :</strong> Subscribers to the <em>"on"</em> moment, will be notified <em>before</em> any actual state change has occurred. This provides the opportunity to prevent the state change from occurring,
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desaiusing the `preventDefault()` method of the event facade object passed to the subscriber. If you use `get()` to retrieve the value of the attribute in an <em>"on"</em> subscriber, you will receive the current, unchanged value.
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen DesaiHowever the event facade provides access to the value which the attribute is being set to, through it's `newVal` property.</p>
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai<p><strong>after :</strong> Subscribers to the <em>"after"</em> moment, will be notified <em>after</em> the attribute's state has been updated.
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen DesaiThis provides the opportunity to update state in other parts of your application, in response to a change in the attribute's state.</p>
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai<p>Based on the definition above, `after` listeners are not invoked if state change is prevented; for example, due to one of the <em>"on"</em> listeners calling `preventDefault()` on the event object passed to the subscriber.</p>
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai<h3>Having Jane React To John</h3>
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai<p>Aside from the internal listeners set up by the class, in this example `jane` also sets up two more subscribers. The first is a subscriber, which allows `jane` to "reconsider" changing the state of her `available` attribute,
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desaiunder certain conditions. Since she may want to prevent the `available` attribute from being modified in this case, we use Attribute's `on()` method to listen for the <em>"on"</em> moment, so that the default behavior can be prevented:</p>
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai```
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai// We can also listen before an attribute changes its value, and
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai// decide if we want to allow the state change to occur or not.
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai// Invoking e.preventDefault() stops the state from being updated.
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai// In this case, Jane can change her mind about making herself
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai// unavailable, if John likes saving whales, as long as he doesn't
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai// dig knitting too.
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desaijane.on("availableChange", function(e) {
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai var johnsInterests = john.get("interests");
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai var janeAvailable = e.newVal;
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai if (janeAvailable === false && Y.Array.indexOf(johnsInterests, "Saving Whales") !== -1
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai && Y.Array.indexOf(johnsInterests, "Knitting") == -1 ) {
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai // Reconsider..
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai e.preventDefault();
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai };
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai});
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai```
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai<p>We also set up an <em>"after"</em> listener on the `john` instance, which allows `jane` to update her interests, so she can admit to enjoying "Reading Specifications", if `john` admits it first:</p>
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai```
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai// Consider updating Jane's interests state, after John's interests
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai// state changes...
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desaijohn.after("interestsChange", function(e) {
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai var janesInterests = jane.get("interests"),
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai // Get john's new interests from the attribute change event...
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai johnsInterests = e.newVal,
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai readingSpecs = "Reading Specifications";
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai // If it turns out that John enjoys reading specs, then Jane can admit it too...
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai if (Y.Array.indexOf(johnsInterests, readingSpecs) !== -1) {
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai if(Y.Array.indexOf(janesInterests, readingSpecs) == -1) {
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai janesInterests.push(readingSpecs);
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai }
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai } else {
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai // Otherwise, we use Y.Array.reject, provided by the "collection" module,
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai // to remove "Reading Specifications" from jane's interests..
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai janesInterests = Y.Array.reject(janesInterests,
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai function(item){return (item == readingSpecs);});
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai }
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai jane.set("interests", janesInterests);
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai jane.set("available", true);
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai ...
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai});
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai```
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai<h3>Event Facade</h3>
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai<p>The event object (an instance of <a href="{{apiDocs}}/EventFacade.html">EventFacade</a>) passed to attribute change event subscribers, has the following interesting properties and methods related to attribute management:</p>
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai<dl>
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai <dt>newVal</dt>
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai <dd>The value which the attribute will be set to (in the case of <em>"on"</em> subscribers), or has been set to (in the case of <em>"after"</em> subscribers</dd>
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai <dt>prevVal</dt>
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai <dd>The value which the attribute is currently set to (in the case of <em>"on"</em> subscribers), or was previously set to (in the case of <em>"after"</em> subscribers</dd>
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai <dt>attrName</dt>
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai <dd>The name of the attribute which is being set</dd>
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai <dt>subAttrName</dt>
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai <dd>Attribute also allows you to set nested properties of attributes which have values which are objects through the
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai `set` method (e.g. `o1.set("x.y.z")`). This property will contain the path to the property which was changed.</dd>
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai <dt>preventDefault()<dt>
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai <dd>This method can be called in an <em>"on"</em> subscriber to prevent the attribute's value from being updated (the default behavior). Calling this method in an <em>"after"</em> listener has no impact, since the default behavior has already been invoked.</dd>
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai <dt>stopImmediatePropagation()</dt>
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai <dd>This method can be called in <em>"on"</em> or <em>"after"</em> subscribers, and will prevent the rest of the subscriber stack from
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai being invoked, but will not prevent the attribute's value from being updated.</dd>
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai</dl>
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai<h2>Complete Example Source</h2>
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai```
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai{{>attribute-event-speeddate-source}}
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai```