|
525
|
1 |
<!DOCTYPE html> |
|
|
2 |
<html lang="en"> |
|
|
3 |
<head> |
|
|
4 |
<meta charset="utf-8"> |
|
|
5 |
<title>Attribute</title> |
|
|
6 |
<link rel="stylesheet" href="http://fonts.googleapis.com/css?family=PT+Sans:400,700,400italic,700italic"> |
|
|
7 |
<link rel="stylesheet" href="../../build/cssgrids/cssgrids-min.css"> |
|
|
8 |
<link rel="stylesheet" href="../assets/css/main.css"> |
|
|
9 |
<link rel="stylesheet" href="../assets/vendor/prettify/prettify-min.css"> |
|
|
10 |
<link rel="shortcut icon" type="image/png" href="../assets/favicon.png"> |
|
|
11 |
<script src="../../build/yui/yui-min.js"></script> |
|
|
12 |
|
|
|
13 |
</head> |
|
|
14 |
<body> |
|
|
15 |
<!-- |
|
|
16 |
<a href="https://github.com/yui/yui3"><img style="position: absolute; top: 0; right: 0; border: 0;" src="https://s3.amazonaws.com/github/ribbons/forkme_right_darkblue_121621.png" alt="Fork me on GitHub"></a> |
|
|
17 |
--> |
|
|
18 |
<div id="doc"> |
|
|
19 |
<div id="hd"> |
|
|
20 |
<h1><img src="http://yuilibrary.com/img/yui-logo.png"></h1> |
|
|
21 |
</div> |
|
|
22 |
|
|
|
23 |
<a href="#toc" class="jump">Jump to Table of Contents</a> |
|
|
24 |
|
|
|
25 |
|
|
|
26 |
<h1>Attribute</h1> |
|
|
27 |
<div class="yui3-g"> |
|
|
28 |
<div class="yui3-u-3-4"> |
|
|
29 |
<div id="main"> |
|
|
30 |
<div class="content"><div class="intro"> |
|
|
31 |
<p>The Attribute utility allows you to add attributes to any class through an augmentable Attribute interface. |
|
|
32 |
The interface adds <code>get and </code>set methods to your class to retrieve and store attribute values, as well as |
|
|
33 |
support for change events that can be used to listen for changes in attribute values.</p> |
|
|
34 |
|
|
|
35 |
<p>In addition, attributes can be configured with custom getters, setters and validators, allowing the developer to |
|
|
36 |
normalize and validate values being stored or retrieved. Attributes can also be specified as read-only or write-once.</p> |
|
|
37 |
</div> |
|
|
38 |
|
|
|
39 |
<h2 id="getting-started">Getting Started</h2> |
|
|
40 |
|
|
|
41 |
<p> |
|
|
42 |
To include the source files for Attribute and its dependencies, first load |
|
|
43 |
the YUI seed file if you haven't already loaded it. |
|
|
44 |
</p> |
|
|
45 |
|
|
|
46 |
<pre class="code prettyprint"><script src="http://yui.yahooapis.com/3.10.3/build/yui/yui-min.js"></script></pre> |
|
|
47 |
|
|
|
48 |
|
|
|
49 |
<p> |
|
|
50 |
Next, create a new YUI instance for your application and populate it with the |
|
|
51 |
modules you need by specifying them as arguments to the <code>YUI().use()</code> method. |
|
|
52 |
YUI will automatically load any dependencies required by the modules you |
|
|
53 |
specify. |
|
|
54 |
</p> |
|
|
55 |
|
|
|
56 |
<pre class="code prettyprint"><script> |
|
|
57 |
// Create a new YUI instance and populate it with the required modules. |
|
|
58 |
YUI().use('attribute', function (Y) { |
|
|
59 |
// Attribute is available and ready for use. Add implementation |
|
|
60 |
// code here. |
|
|
61 |
}); |
|
|
62 |
</script></pre> |
|
|
63 |
|
|
|
64 |
|
|
|
65 |
<p> |
|
|
66 |
For more information on creating YUI instances and on the |
|
|
67 |
<a href="http://yuilibrary.com/yui/docs/api/classes/YUI.html#method_use"><code>use()</code> method</a>, see the |
|
|
68 |
documentation for the <a href="../yui/index.html">YUI Global Object</a>. |
|
|
69 |
</p> |
|
|
70 |
|
|
|
71 |
|
|
|
72 |
<h2 id="augment">Augmenting Your Class With Attribute</h2> |
|
|
73 |
|
|
|
74 |
<p>The Attribute class is designed to be augmented to an existing class (we'll refer to this class as the 'host' class) and adds attribute |
|
|
75 |
management support to it. For example, assuming you have a class constructor, `MyClass, to which you'd like to add attribute support, |
|
|
76 |
you can simply augment your class with Attribute, as shown below:</p> |
|
|
77 |
|
|
|
78 |
<pre class="code prettyprint">YUI().use("attribute", function(Y) { |
|
|
79 |
|
|
|
80 |
function MyClass() { |
|
|
81 |
... |
|
|
82 |
} |
|
|
83 |
|
|
|
84 |
Y.augment(MyClass, Y.Attribute); |
|
|
85 |
});</pre> |
|
|
86 |
|
|
|
87 |
<p>Instances of your class will now have Attribute methods available, which your class can use to configure attributes for itself, and which users of your |
|
|
88 |
class can use to get and set attribute values. See the <a href="http://yuilibrary.com/yui/docs/api/Attribute.html">Attribute API documentation</a> for a complete list of |
|
|
89 |
methods which Attribute will add to your class.</p> |
|
|
90 |
|
|
|
91 |
<p>Note that in general, rather than augmenting Attribute directly, most implementations will simply extend <a href="../base/index.html">Base</a>, which |
|
|
92 |
augments Attribute, and handles attribute setup for you. Base also sets up all attributes to be lazily initialized (initialized on the first call to get or set) by default, improving performance.</p> |
|
|
93 |
|
|
|
94 |
<h2 id="adding">Adding Attributes</h2> |
|
|
95 |
|
|
|
96 |
<p>Once augmented with Attribute, your class can either use the <code>addAttrs</code> method to setup attributes en mass, or use the |
|
|
97 |
<code>addAttr</code> method, to add them individually. <code>addAttrs</code> is tailored towards use by host classes, since in addition |
|
|
98 |
to being able to initialize multiple attributes in one call, it also accepts an additional name/value hash, which can be used to allow the |
|
|
99 |
user to define the initial value of attributes when instantiating Attribute driven classes, as shown below:</p> |
|
|
100 |
|
|
|
101 |
<pre class="code prettyprint">... |
|
|
102 |
function MyClass(userValues) { |
|
|
103 |
|
|
|
104 |
// Use addAttrs, to setup default attributes for |
|
|
105 |
// your class, and mixing in user provided initial values. |
|
|
106 |
|
|
|
107 |
var attributeConfig = { |
|
|
108 |
attrA : { |
|
|
109 |
// ... Configuration for attribute "attrA" ... |
|
|
110 |
}, |
|
|
111 |
|
|
|
112 |
attrB : { |
|
|
113 |
// ... Configuration for attribute "attrB" ... |
|
|
114 |
} |
|
|
115 |
}; |
|
|
116 |
|
|
|
117 |
this.addAttrs(attributeConfig, userValues); |
|
|
118 |
};</pre> |
|
|
119 |
|
|
|
120 |
|
|
|
121 |
<p>Users of your class now have the ability to pass attribute values to the constructor, |
|
|
122 |
or set values using the <code>set</code> method as shown below:</p> |
|
|
123 |
|
|
|
124 |
<pre class="code prettyprint">// Set initial value for attrA during instantiation |
|
|
125 |
var o = new MyClass({ |
|
|
126 |
attrA:5 |
|
|
127 |
}); |
|
|
128 |
|
|
|
129 |
// Set attrB later on |
|
|
130 |
o.set("attrB", "Hello World!");</pre> |
|
|
131 |
|
|
|
132 |
|
|
|
133 |
<h2 id="configuration">Attribute Configuration Properties</h2> |
|
|
134 |
|
|
|
135 |
<p>Each attribute you add can be configured with the properties listed in the table below (all properties are optional and case-sensitive):</p> |
|
|
136 |
|
|
|
137 |
<table> |
|
|
138 |
<caption>Attribute Configuration Properties</caption> |
|
|
139 |
<thead> |
|
|
140 |
<tr> |
|
|
141 |
<th>Property Name</th> |
|
|
142 |
<th>Type</th> |
|
|
143 |
<th>Description</th> |
|
|
144 |
</tr> |
|
|
145 |
</thead> |
|
|
146 |
<tbody> |
|
|
147 |
<tr> |
|
|
148 |
<td><code>value</code></td> |
|
|
149 |
<td>Any</td> |
|
|
150 |
<td>The default value for this attribute</td> |
|
|
151 |
</tr> |
|
|
152 |
<tr> |
|
|
153 |
<td><code>valueFn</code></td> |
|
|
154 |
<td>Function</td> |
|
|
155 |
<td> |
|
|
156 |
|
|
|
157 |
<p>A function, the return value of which is the value for the attribute. This property can be used instead of the |
|
|
158 |
value property for static configurations, if you need to set default values which require access to instance state |
|
|
159 |
("this.something").</p> |
|
|
160 |
|
|
|
161 |
<p>If both the value and valueFn properties are defined, the value returned by valueFn has precedence over the value property, |
|
|
162 |
unless it returns undefined, in which case the value property is used.</p> |
|
|
163 |
|
|
|
164 |
<p>The valueFn is passed the name of the attribute, allowing users to share valueFn implementations across attributes if required.</p> |
|
|
165 |
</td> |
|
|
166 |
</tr> |
|
|
167 |
<tr> |
|
|
168 |
<td><code>getter</code></td> |
|
|
169 |
<td>Function</td> |
|
|
170 |
<td> |
|
|
171 |
<p>Custom 'get' handler, which is invoked when the user calls Attribute's <code>get</code> method. It can |
|
|
172 |
be used to manipulate or normalize the stored value before it is returned to the user.</p> |
|
|
173 |
|
|
|
174 |
<p>The function will be passed the currently stored value of the attribute as the first argument and the name |
|
|
175 |
of the attribute as the second argument. If configured, the value returned by this function will be |
|
|
176 |
the value returned to the user.</p> |
|
|
177 |
|
|
|
178 |
<p>Attribute also supports the ability to return sub-attribute values (<code>get('a.b.c')</code>). The getter implications for |
|
|
179 |
this are discussed in the <a href="#subattrs-gsv">Getters, Setters, Validators and Sub Attributes</a> section.</p> |
|
|
180 |
</td> |
|
|
181 |
</tr> |
|
|
182 |
<tr> |
|
|
183 |
<td><code>setter</code></td> |
|
|
184 |
<td>Function</td> |
|
|
185 |
<td> |
|
|
186 |
<p>Custom 'set' handler, which is invoked when the user calls Attribute's <code>set</code> method. It can |
|
|
187 |
be used to manipulate the value which is stored for the attribute.</p> |
|
|
188 |
|
|
|
189 |
<p>The function will be passed the value which the user passed to the set method as the first |
|
|
190 |
argument, the name of the attribute as the second argument and the third argument passed to the <code>set</code> |
|
|
191 |
method as the third argument. If configured, the value returned by this |
|
|
192 |
function will be the value stored as the attribute value.</p> |
|
|
193 |
|
|
|
194 |
<p>The setter may return the constant <code>Y.Attribute.INVALID_VALUE</code> to reject a value.</p> |
|
|
195 |
|
|
|
196 |
<p>The getter and setter can be used to normalize values on input/output for the user while storing the |
|
|
197 |
value in a format most effective for internal operation.</p> |
|
|
198 |
|
|
|
199 |
<p>Attribute also supports the ability to set sub-attribute values (<code>set('a.b.c', 10)</code>). The setter |
|
|
200 |
implications for this are discussed in the <a href="#subattrs-gsv">Getters, Setters, Validators and Sub Attributes</a> |
|
|
201 |
section.</p> |
|
|
202 |
|
|
|
203 |
<p>The third argument is optional and it should have been an object. It may be used to give the reason or origin of |
|
|
204 |
the change. This allows the setter to manipulate the value in special ways when it comes from certain |
|
|
205 |
sources or to decide whether to accept it or not.</p> |
|
|
206 |
|
|
|
207 |
</td> |
|
|
208 |
</tr> |
|
|
209 |
<tr> |
|
|
210 |
<td><code>validator</code></td> |
|
|
211 |
<td>Function</td> |
|
|
212 |
<td> |
|
|
213 |
<p>Validation function, which if defined, is called before the <code>setter</code>. |
|
|
214 |
The validation function is passed the value which the user is trying to set as the first argument, the name of the |
|
|
215 |
attribute as the second argument and the third argument passed to the <code>set</code> method |
|
|
216 |
as the third argument.</p> |
|
|
217 |
|
|
|
218 |
<p>If the function returns <code>false</code>, the attribute's stored value is not updated (and the <code>setter</code>, if defined, will not be invoked). |
|
|
219 |
If it returns <code>true</code>, the <code>setter</code> is invoked if defined, and the attribute's stored value is updated.</p> |
|
|
220 |
|
|
|
221 |
<p>If validation is a potentially expensive task and contains code which would be repeated in a <code>setter</code> (for example, converting a string to a Node reference), |
|
|
222 |
you can combine validation into the <code>setter</code> function, by returning <code>Attribute.INVALID_VALUE</code> from a <code>setter</code> if it |
|
|
223 |
encounters an invalid value.</p> |
|
|
224 |
|
|
|
225 |
<p>Attribute also supports the ability to set sub-attribute values (set('a.b.c', 10)). The 'validator' |
|
|
226 |
implications for this are discussed in the <a href="#subattrs-gsv">Getters, Setters, Validators and Sub Attributes</a> |
|
|
227 |
section.</p> |
|
|
228 |
|
|
|
229 |
<p>The third argument is optional and it should have been an object. It may be used to give the reason or origin of |
|
|
230 |
the change. This allows the validator to accept a value when it comes from certain sources |
|
|
231 |
while rejecting it in general.</p> |
|
|
232 |
</td> |
|
|
233 |
</tr> |
|
|
234 |
<tr> |
|
|
235 |
<td><code>readOnly</code></td> |
|
|
236 |
<td>boolean</td> |
|
|
237 |
<td> |
|
|
238 |
Configures the attribute to be read-only. Users will not be able to set the value of the attribute using |
|
|
239 |
Attribute's public API. |
|
|
240 |
</td> |
|
|
241 |
</tr> |
|
|
242 |
<tr> |
|
|
243 |
<td><code>writeOnce</code></td> |
|
|
244 |
<td>boolean or "initOnly"</td> |
|
|
245 |
<td> |
|
|
246 |
<p>Configures the attribute to be write-once. Users will only be able to set the value of the attribute using |
|
|
247 |
Attribute's <code>set</code> method once. Once a value has been set for the attribute, calling <code>set</code> will not change it's value. |
|
|
248 |
</p> |
|
|
249 |
<p>If set to "initOnly", the attribute can only be set during initialization. In the case of Base, this means that the attribute can |
|
|
250 |
only be set through the constructor.</p> |
|
|
251 |
<p>Code within your class can update the value of readOnly or writeOnce attributes by using the private <code>_set</code> method.</p> |
|
|
252 |
</td> |
|
|
253 |
</tr> |
|
|
254 |
<tr> |
|
|
255 |
<td><code>broadcast</code></td> |
|
|
256 |
<td>int</td> |
|
|
257 |
<td> |
|
|
258 |
By default attribute change events are not broadcast to the YUI instance or global YUI object. The broadcast property |
|
|
259 |
can be used to set specific attribute change events to be broadcast to either the YUI instance or the global YUI object. |
|
|
260 |
See CustomEvent's <a href="http://yuilibrary.com/yui/docs/api/CustomEvent.html#property_broadcast">broadcast</a> property for valid values. |
|
|
261 |
</td> |
|
|
262 |
</tr> |
|
|
263 |
<tr> |
|
|
264 |
<td><code>lazyAdd</code></td> |
|
|
265 |
<td>boolean</td> |
|
|
266 |
<td> |
|
|
267 |
<p> |
|
|
268 |
Whether or not to delay initialization of the attribute until the first call to get/set it. |
|
|
269 |
This flag can be used to over-ride lazy initialization on a per attribute basis, when adding multiple attributes through |
|
|
270 |
the <a href="http://yuilibrary.com/yui/docs/api/Attribute.html#method_addAttrs"><code>addAttrs</code></a> method. |
|
|
271 |
</p> |
|
|
272 |
<p>When extending Base, all attributes are added lazily, so this flag can be used to over-ride |
|
|
273 |
lazyAdd behavior for specific attributes.</p> |
|
|
274 |
<p><strong>The only reason you should need to disable <code>lazyAdd</code> is if your setter is doing more |
|
|
275 |
than normalizing the attribute value</strong>. For example if your setter is storing some other state, in <code>this._someProp</code>, which is being used |
|
|
276 |
by other parts of your component. In this case, since you're not getting or setting the attribute to access <code>this._someProp</code>, |
|
|
277 |
it won't get set up lazily, until someone actually calls <code>get</code> or <code>set</code> for the related attribute.</p> |
|
|
278 |
</td> |
|
|
279 |
</tr> |
|
|
280 |
<tr> |
|
|
281 |
<td><code>cloneDefaultValue</code></td> |
|
|
282 |
<td>"shallow", "deep", true, false</td> |
|
|
283 |
<td> |
|
|
284 |
<p> |
|
|
285 |
This configuration value is not actually supported by Attribute natively, but is available when |
|
|
286 |
working with Base, and defining attribute configurations using Base's static <a href="http://yuilibrary.com/yui/docs/api/Base.html#property_ATTRS">ATTRS</a> property. |
|
|
287 |
</p> |
|
|
288 |
<p> |
|
|
289 |
This property controls how the statically defined default <code>value</code> field in Base's <code>ATTRS</code> attribute configuration is handled, |
|
|
290 |
when setting it up as the value for an instance. By default (if this property is not defined) object literals and arrays are deep cloned, to protect the default value from |
|
|
291 |
being modified. Setting cloneDefaultValue to <code>false</code> will disable cloning. This is useful in cases where you intend to use arrays or |
|
|
292 |
object literals by reference (e.g. they point to utilities). A shallow clone will be used if cloneDefaultValue is set to <code>"shallow"</code> and a |
|
|
293 |
deep clone will be used for <code>"deep"</code> or <code>true</code>. |
|
|
294 |
</p> |
|
|
295 |
</td> |
|
|
296 |
</tr> |
|
|
297 |
</tbody> |
|
|
298 |
</table> |
|
|
299 |
|
|
|
300 |
<h4 id="howtoconfig">Configuring Attributes</h4> |
|
|
301 |
|
|
|
302 |
<p>The above attribute properties are set using an object with property name/value pairs, which is passed to either <code>addAttrs</code> or <code>addAttr</code> as the |
|
|
303 |
configuration argument. For example, expanding on the code snippet above:</p> |
|
|
304 |
|
|
|
305 |
<pre class="code prettyprint">... |
|
|
306 |
var attributeConfig = { |
|
|
307 |
attrA : { |
|
|
308 |
// Configuration for attribute "attrA" |
|
|
309 |
|
|
|
310 |
value: 5, |
|
|
311 |
|
|
|
312 |
setter: function(val) { |
|
|
313 |
return Math.min(val, 10); |
|
|
314 |
}, |
|
|
315 |
|
|
|
316 |
validator: function(val) { |
|
|
317 |
return Y.Lang.isNumber(val); |
|
|
318 |
} |
|
|
319 |
}, |
|
|
320 |
|
|
|
321 |
attrB : { |
|
|
322 |
// Configuration for attribute "attrB" |
|
|
323 |
} |
|
|
324 |
}; |
|
|
325 |
|
|
|
326 |
this.addAttrs(attributeConfig, userValues); |
|
|
327 |
...</pre> |
|
|
328 |
|
|
|
329 |
<p>Or, if using <code>addAttr</code>:</p> |
|
|
330 |
|
|
|
331 |
<pre class="code prettyprint">this.addAttr("attrA", { |
|
|
332 |
// Configuration for attribute "attrA" |
|
|
333 |
|
|
|
334 |
value: 5, |
|
|
335 |
|
|
|
336 |
setter: function(val) { |
|
|
337 |
return Math.min(val, 10); |
|
|
338 |
}, |
|
|
339 |
|
|
|
340 |
validator: function(val) { |
|
|
341 |
return Y.Lang.isNumber(val); |
|
|
342 |
} |
|
|
343 |
});</pre> |
|
|
344 |
|
|
|
345 |
<h2 id="setting-and-getting-attributes">Setting and Getting Attributes</h2> |
|
|
346 |
|
|
|
347 |
<p>Attribute adds <code>set</code> and <code>get</code> methods to the object it augments.</p> |
|
|
348 |
<pre class="code prettyprint">myObject.set("attrA", 6); |
|
|
349 |
|
|
|
350 |
Y.log(myObject.get("attrA")); // should log 6</pre> |
|
|
351 |
|
|
|
352 |
<p>Several attributes can be set or read at once via the <code>setAttrs</code> and <code>getAttrs</code> |
|
|
353 |
methods.</p> |
|
|
354 |
<pre class="code prettyprint">myObject.setAttrs({ |
|
|
355 |
age: 6, |
|
|
356 |
name: "John" |
|
|
357 |
}); |
|
|
358 |
|
|
|
359 |
Y.log(Y.Lang.sub("{name} is {age} years old", myObject.getAttrs()));</pre> |
|
|
360 |
|
|
|
361 |
<p>Calling <code>getAttrs</code> without any arguments will return all the configured attributes. |
|
|
362 |
Passing an array of attribute names will return only those. Passing <code>true</code> |
|
|
363 |
will return only those modified from its initially configured value.</p> |
|
|
364 |
|
|
|
365 |
<p>A protected <code>_set</code> method allows changing the value of attributes configured as <code>readOnly</code> |
|
|
366 |
or past the first write in those configured as <code>writeOnce</code>, to allow the |
|
|
367 |
object to still enforce those rules in the public API while allowing the |
|
|
368 |
object to change the values internally.</p> |
|
|
369 |
|
|
|
370 |
<p>An extra argument can be provided to <code>set</code> and <code>setAttrs</code> indicating the |
|
|
371 |
reason or origin of the change. This argument must be an object and it will |
|
|
372 |
be passed to the <code>setter</code> and/or <code>validator</code> methods, if any, and it will |
|
|
373 |
be merged into the event facade of the attribute change events.</p> |
|
|
374 |
|
|
|
375 |
<pre class="code prettyprint">myObject.set("attrA", inputNode.get("value"), {src:"UI"}); |
|
|
376 |
|
|
|
377 |
myObject.setAttrs({ |
|
|
378 |
attrA: null, |
|
|
379 |
attrB: "" |
|
|
380 |
},{src:"internal"});</pre> |
|
|
381 |
|
|
|
382 |
|
|
|
383 |
<p>This may allow an object to set an attribute to a value otherwise forbidden |
|
|
384 |
in the public API, to have the <code>setter</code> manipulate it in a special way or |
|
|
385 |
an attribute change event to respond in a particular manner. For example, as shown |
|
|
386 |
in the sample code above, |
|
|
387 |
a <code>setter</code> may accept and parse a numeric string when the the source of the |
|
|
388 |
change is the UI while it would reject it otherwise.</p> |
|
|
389 |
|
|
|
390 |
<h2 id="events">Attribute Change Events</h2> |
|
|
391 |
|
|
|
392 |
<p>The availability of attribute change events are one of the key benefits of using |
|
|
393 |
attributes to store state for your objects, instead of regular object properties. |
|
|
394 |
Attribute change events are fired whenever <code>set</code> is invoked for an |
|
|
395 |
attribute, allowing you to execute code in response to a change in the attribute's |
|
|
396 |
value.</p> |
|
|
397 |
|
|
|
398 |
<h4 id="listening-for-change-events">Listening for Change Events</h4> |
|
|
399 |
|
|
|
400 |
<p>Attribute change events are Custom Events, having the type: "[attributeName]Change", where |
|
|
401 |
[attributeName] is the name of the attribute which you're monitoring for changes.</p> |
|
|
402 |
|
|
|
403 |
<p>For example, if you were interested in listening for changes to an attribute named |
|
|
404 |
"enabled", you would subscribe to events of type "enabledChange".</p> |
|
|
405 |
|
|
|
406 |
<pre class="code prettyprint">o.on("enabledChange", function(event) { |
|
|
407 |
// Do something just before "enabled" is about to be set |
|
|
408 |
});</pre> |
|
|
409 |
|
|
|
410 |
|
|
|
411 |
<p><em>NOTE:</em> Context and additional arguments for the listener function can either be |
|
|
412 |
defined using YUI's <code>bind</code> method, or by passing in the context and |
|
|
413 |
additional arguments to the <code>on</code> method.</p> |
|
|
414 |
|
|
|
415 |
<p>Attribute change event listeners can be registered using either the <code>on</code> |
|
|
416 |
(as shown above) or <code>after</code> Attribute methods.</p> |
|
|
417 |
|
|
|
418 |
<p>Attribute change event listeners may respond differently according to the |
|
|
419 |
source or reason of the change as specified in the third argument to <code>set</code>. |
|
|
420 |
This argument will be merged into the event facade and can be checked by the |
|
|
421 |
event listener.</p> |
|
|
422 |
<pre class="code prettyprint">myObject.after("nameChange", function (event) { |
|
|
423 |
if (event.src !== "UI") { |
|
|
424 |
inputNode.set("value", event.newVal); |
|
|
425 |
} |
|
|
426 |
});</pre> |
|
|
427 |
|
|
|
428 |
<p>The code above listens to changes in the <code>name</code> attribute and, if it comes |
|
|
429 |
from anywhere but the UI, it sets the input box to the new value.</p> |
|
|
430 |
|
|
|
431 |
<h4 id="on-vs-after">On vs. After</h4> |
|
|
432 |
|
|
|
433 |
<h5 id="on">On</h5> |
|
|
434 |
|
|
|
435 |
<p>Listeners registered using the <code>on</code> method, are notified <strong>before</strong> the stored state of the attribute has been updated. |
|
|
436 |
Functions registered as "on" listeners receive an <code>Event</code> object as the first argument (actually an |
|
|
437 |
instance of <a href="http://yuilibrary.com/yui/docs/api/EventFacade.html"><code>EventFacade</code></a>) which contains information |
|
|
438 |
about the attribute being modified.</p> |
|
|
439 |
|
|
|
440 |
<p>Since these listeners are invoked before any state change has occurred, they have the ability to |
|
|
441 |
prevent the change in state from occurring, by invoking <code>event.preventDefault()</code> on |
|
|
442 |
the event object passed to them, or to modify the value being set, by modifying the <code>event.newVal</code> property.</p> |
|
|
443 |
|
|
|
444 |
<p>The value passed to the "on" event listener has not yet been checked via the <code>validator</code> or |
|
|
445 |
normalized by the <code>setter</code> method. Since the change may later be rejected or prevented |
|
|
446 |
by further event listeners attached later, an "on" event listener should not |
|
|
447 |
produce any secondary effects.</p> |
|
|
448 |
|
|
|
449 |
<pre class="code prettyprint">o.on("enabledChange", function(event) { |
|
|
450 |
|
|
|
451 |
// event.prevVal will contain the current attribute value |
|
|
452 |
var val = event.prevVal; |
|
|
453 |
|
|
|
454 |
if (val !== someCondition) { |
|
|
455 |
// Prevent "enabled" from being changed |
|
|
456 |
event.preventDefault(); |
|
|
457 |
} |
|
|
458 |
|
|
|
459 |
});</pre> |
|
|
460 |
|
|
|
461 |
|
|
|
462 |
<h5 id="after">After</h5> |
|
|
463 |
|
|
|
464 |
<p>Listeners registered using the <code>after</code> method, are notified <strong>after</strong> |
|
|
465 |
the stored state of the attribute has been updated. As with "on" listeners, the subscribed function |
|
|
466 |
receives an <code>Event</code> object as the first parameter.</p> |
|
|
467 |
|
|
|
468 |
<p>Based on the definition above, "after" listeners are not invoked if state change is prevented, |
|
|
469 |
for example, due to one of the "on" listeners calling <code>preventDefault</code> on the event object. |
|
|
470 |
They are not invoked either if the <code>validator</code> or <code>setter</code> methods |
|
|
471 |
rejected the value and they will receive the value already modified by the <code>setter</code> thus |
|
|
472 |
it is safe to produce any secondary effects based on the changed value.</p> |
|
|
473 |
|
|
|
474 |
<pre class="code prettyprint">o.after("enabledChange", function(event) { |
|
|
475 |
|
|
|
476 |
// event.newVal will contain the currently set value |
|
|
477 |
var val = event.newVal; |
|
|
478 |
|
|
|
479 |
// Calling preventDefault() in an "after" listener has no impact |
|
|
480 |
event.preventDefault(); |
|
|
481 |
|
|
|
482 |
});</pre> |
|
|
483 |
|
|
|
484 |
|
|
|
485 |
|
|
|
486 |
<h4 id="the-event-object">The Event Object</h4> |
|
|
487 |
|
|
|
488 |
<p>The event facade passed to attribute change event listeners, has a number of attributes which provide information about the attribute being modified, |
|
|
489 |
as well methods used to manage event propagation. These are described below:</p> |
|
|
490 |
|
|
|
491 |
<dl> |
|
|
492 |
<dt>newVal</dt> |
|
|
493 |
<dd>The value which the attribute will be set to (in the case of "on" listeners), or has been set to (in the case of "after" listeners)</dd> |
|
|
494 |
<dt>prevVal</dt> |
|
|
495 |
<dd>The value which the attribute is currently set to (in the case of "on" listeners), or was previously set to (in the case of "after" listeners)</dd> |
|
|
496 |
<dt>attrName</dt> |
|
|
497 |
<dd>The name of the attribute which is being set</dd> |
|
|
498 |
<dt>subAttrName</dt> |
|
|
499 |
<dd><p>Attribute also allows you to set individual properties of attributes having values which are objects through the |
|
|
500 |
<code>set</code> method (e.g. <code>o.set("X.a.b", 5)</code>, discussed below). This event property will contain the complete dot notation path for the object property which was changed.</p> |
|
|
501 |
<p>For example, during <code>o.set("X.a.b", 5);</code>, <code>event.subAttrName</code> will be <code>"X.a.b"</code>, the path of the property which was modified, and <code>event.attrName</code> will be <code>"X"</code>, the attribute name.</p></dd> |
|
|
502 |
<dt>preventDefault()<dt> |
|
|
503 |
<dd>This method can be called in an "on" listener function 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> |
|
|
504 |
<dt>stopImmediatePropagation()</dt> |
|
|
505 |
<dd>This method can be called in "on" or "after" listener functions, and will prevent the rest of the listener stack from |
|
|
506 |
being notified, but will not prevent the attribute's value from being updated (viz. will not prevent the default behavior).</dd> |
|
|
507 |
<dt>-- Custom properties --</dt> |
|
|
508 |
<dd>Any other properties in the object passed as the last argument in a <code>set</code> or <code>setAttrs</code> call.</dd> |
|
|
509 |
</dl> |
|
|
510 |
|
|
|
511 |
<h2 id="attrsetflow">Attribute Set Flow Diagram</h2> |
|
|
512 |
|
|
|
513 |
<p>The diagram below shows the order in which attribute setters, validators and change event subscribers are invoked during the set operation:</p> |
|
|
514 |
|
|
|
515 |
<p><a href="setflow.html" title="Click too see full-size image"><img src="../assets/attribute/img/attribute-set-flow.png" alt="Flow diagram for the attribute 'set' operation" height="466" width="538"></a></p> |
|
|
516 |
|
|
|
517 |
<p><em>NOTE:</em> Any decision blocks for which an exit path is not explicitly shown will effectively exit the set operation, without storing the new value. These paths are not explicitly shown, in order to avoid clutter.</p> |
|
|
518 |
|
|
|
519 |
<h2 id="subattrs">Getting/Setting Sub Attribute Values</h2> |
|
|
520 |
|
|
|
521 |
<p>If you have attribute values which are objects (as opposed to primitive values, such as numbers or booleans), the <code>set</code> method will let |
|
|
522 |
you set properties of the object directly, using a dot notation syntax. For example, if you have an attribute, "strings" with the following value:</p> |
|
|
523 |
|
|
|
524 |
<pre class="code prettyprint">o.set("strings", { |
|
|
525 |
ui : { |
|
|
526 |
accept_label : "OK", |
|
|
527 |
decline_label : "Cancel", |
|
|
528 |
}, |
|
|
529 |
errors : { |
|
|
530 |
e1000 : "Not Supported", |
|
|
531 |
e1001 : "Network Error" |
|
|
532 |
} |
|
|
533 |
});</pre> |
|
|
534 |
|
|
|
535 |
|
|
|
536 |
<p>You can set individual properties on the "strings" attribute value object, without having to get and then set the whole string's attribute value. This is done |
|
|
537 |
by using the dot notation to reference properties within the attribute's value:</p> |
|
|
538 |
|
|
|
539 |
<pre class="code prettyprint">// Set existing properties |
|
|
540 |
o.set("strings.ui.accept_label", "Yes"); |
|
|
541 |
o.set("strings.ui.decline_label", "No"); |
|
|
542 |
|
|
|
543 |
// Add a new property |
|
|
544 |
o.set("strings.errors.e2000", "New Error"); |
|
|
545 |
|
|
|
546 |
// Cannot set new intermediate properties: |
|
|
547 |
// "strings.messages" does not exist so can't set |
|
|
548 |
// "strings.messages.intro" |
|
|
549 |
o.set("strings.messages.intro", "Welcome");</pre> |
|
|
550 |
|
|
|
551 |
|
|
|
552 |
<p>Setting sub attribute values, will fire an attribute change event for the |
|
|
553 |
main attribute (<code>"stringsChange"</code> in the above example), however the event object passed |
|
|
554 |
to the listeners with have a "subAttrName" property set to reflect the full path to the |
|
|
555 |
attribute set (e.g. <code>event.subAttrName</code> will be <code>"strings.ui.accept_label"</code> for the set call on line 2 in the code snippet above).</p> |
|
|
556 |
|
|
|
557 |
<p>You can also retrieve sub attribute values using the same dot notation syntax</p> |
|
|
558 |
|
|
|
559 |
<pre class="code prettyprint">// Get the string for the accept label |
|
|
560 |
var lbl = o.get("strings.ui.accept_label");</pre> |
|
|
561 |
|
|
|
562 |
|
|
|
563 |
<h3 id="subattrs-gsv">Getters, Setters, Validators and Sub Attributes</h3> |
|
|
564 |
|
|
|
565 |
<p>Getter, setter and validator attribute configuration functions are only defined for the top level |
|
|
566 |
attribute (<code>"strings"</code> in this case) and will be invoked when getting/setting sub attribute values. |
|
|
567 |
What this means is that getters and setters should always return the massaged value for the top level |
|
|
568 |
attribute (e.g. <code>"strings"</code>), and not the sub attribute value being set (e.g. <code>"strings.ui.accept_label"</code>). |
|
|
569 |
The sub attribute being set is passed in as the second argument to the getter/setter/validator, so that it |
|
|
570 |
can fork for sub attribute handling if required.</p> |
|
|
571 |
|
|
|
572 |
<p>For example:</p> |
|
|
573 |
|
|
|
574 |
<pre class="code prettyprint">this.addAttr("strings", { |
|
|
575 |
getter : function(val, fullName) { |
|
|
576 |
|
|
|
577 |
// 'fullName' is "strings.errors.e4500" |
|
|
578 |
// 'val' is the whole strings hash: { ui : {...}, errors : {...}}. |
|
|
579 |
|
|
|
580 |
// 'path' is ["strings", "errors", "e4500"] |
|
|
581 |
var path = fullName.split("."); |
|
|
582 |
|
|
|
583 |
if (path.length > 1) { |
|
|
584 |
|
|
|
585 |
// Someone's asking for a sub-attribute value |
|
|
586 |
|
|
|
587 |
// Maybe we want to do some special normalization just for this use case. |
|
|
588 |
// For example, return a default error message instead of undefined. |
|
|
589 |
|
|
|
590 |
path.shift(); |
|
|
591 |
|
|
|
592 |
if (path[0] == "errors") { |
|
|
593 |
var leafValue = Y.Object.getValue(val, path); |
|
|
594 |
|
|
|
595 |
if (leafValue === undefined) { |
|
|
596 |
Y.Object.setValue(val, path, "Unknown Error"); |
|
|
597 |
} |
|
|
598 |
} |
|
|
599 |
} |
|
|
600 |
|
|
|
601 |
// In either case (sub attribute or not) val is always the full hash for the 'strings' attribute. |
|
|
602 |
return val; |
|
|
603 |
} |
|
|
604 |
});</pre> |
|
|
605 |
|
|
|
606 |
|
|
|
607 |
|
|
|
608 |
</div> |
|
|
609 |
</div> |
|
|
610 |
</div> |
|
|
611 |
|
|
|
612 |
<div class="yui3-u-1-4"> |
|
|
613 |
<div class="sidebar"> |
|
|
614 |
|
|
|
615 |
<div id="toc" class="sidebox"> |
|
|
616 |
<div class="hd"> |
|
|
617 |
<h2 class="no-toc">Table of Contents</h2> |
|
|
618 |
</div> |
|
|
619 |
|
|
|
620 |
<div class="bd"> |
|
|
621 |
<ul class="toc"> |
|
|
622 |
<li> |
|
|
623 |
<a href="#getting-started">Getting Started</a> |
|
|
624 |
</li> |
|
|
625 |
<li> |
|
|
626 |
<a href="#augment">Augmenting Your Class With Attribute</a> |
|
|
627 |
</li> |
|
|
628 |
<li> |
|
|
629 |
<a href="#adding">Adding Attributes</a> |
|
|
630 |
</li> |
|
|
631 |
<li> |
|
|
632 |
<a href="#configuration">Attribute Configuration Properties</a> |
|
|
633 |
<ul class="toc"> |
|
|
634 |
<li> |
|
|
635 |
<a href="#howtoconfig">Configuring Attributes</a> |
|
|
636 |
</li> |
|
|
637 |
</ul> |
|
|
638 |
</li> |
|
|
639 |
<li> |
|
|
640 |
<a href="#setting-and-getting-attributes">Setting and Getting Attributes</a> |
|
|
641 |
</li> |
|
|
642 |
<li> |
|
|
643 |
<a href="#events">Attribute Change Events</a> |
|
|
644 |
<ul class="toc"> |
|
|
645 |
<li> |
|
|
646 |
<a href="#listening-for-change-events">Listening for Change Events</a> |
|
|
647 |
</li> |
|
|
648 |
<li> |
|
|
649 |
<a href="#on-vs-after">On vs. After</a> |
|
|
650 |
<ul class="toc"> |
|
|
651 |
<li> |
|
|
652 |
<a href="#on">On</a> |
|
|
653 |
</li> |
|
|
654 |
<li> |
|
|
655 |
<a href="#after">After</a> |
|
|
656 |
</li> |
|
|
657 |
</ul> |
|
|
658 |
</li> |
|
|
659 |
<li> |
|
|
660 |
<a href="#the-event-object">The Event Object</a> |
|
|
661 |
</li> |
|
|
662 |
</ul> |
|
|
663 |
</li> |
|
|
664 |
<li> |
|
|
665 |
<a href="#attrsetflow">Attribute Set Flow Diagram</a> |
|
|
666 |
</li> |
|
|
667 |
<li> |
|
|
668 |
<a href="#subattrs">Getting/Setting Sub Attribute Values</a> |
|
|
669 |
<ul class="toc"> |
|
|
670 |
<li> |
|
|
671 |
<a href="#subattrs-gsv">Getters, Setters, Validators and Sub Attributes</a> |
|
|
672 |
</li> |
|
|
673 |
</ul> |
|
|
674 |
</li> |
|
|
675 |
</ul> |
|
|
676 |
</div> |
|
|
677 |
</div> |
|
|
678 |
|
|
|
679 |
|
|
|
680 |
|
|
|
681 |
<div class="sidebox"> |
|
|
682 |
<div class="hd"> |
|
|
683 |
<h2 class="no-toc">Examples</h2> |
|
|
684 |
</div> |
|
|
685 |
|
|
|
686 |
<div class="bd"> |
|
|
687 |
<ul class="examples"> |
|
|
688 |
|
|
|
689 |
|
|
|
690 |
<li data-description="Use the Attribute API to define, set and get attribute values."> |
|
|
691 |
<a href="attribute-basic.html">Basic Attribute Configuration</a> |
|
|
692 |
</li> |
|
|
693 |
|
|
|
694 |
|
|
|
695 |
|
|
|
696 |
<li data-description="Configure attributes to be readOnly or writeOnce."> |
|
|
697 |
<a href="attribute-rw.html">Read-Only and Write-Once Attributes</a> |
|
|
698 |
</li> |
|
|
699 |
|
|
|
700 |
|
|
|
701 |
|
|
|
702 |
<li data-description="How to listen for changes in attribute values."> |
|
|
703 |
<a href="attribute-event.html">Attribute Change Events</a> |
|
|
704 |
</li> |
|
|
705 |
|
|
|
706 |
|
|
|
707 |
|
|
|
708 |
<li data-description="Create a basic SpeedDater class, with Attribute support."> |
|
|
709 |
<a href="attribute-basic-speeddate.html">Attribute Based Speed Dating</a> |
|
|
710 |
</li> |
|
|
711 |
|
|
|
712 |
|
|
|
713 |
|
|
|
714 |
<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."> |
|
|
715 |
<a href="attribute-event-speeddate.html">Attribute Event Based Speed Dating</a> |
|
|
716 |
</li> |
|
|
717 |
|
|
|
718 |
|
|
|
719 |
|
|
|
720 |
<li data-description="Add custom methods to get and set attribute values and provide validation support."> |
|
|
721 |
<a href="attribute-getset.html">Attribute Getters, Setters and Validators</a> |
|
|
722 |
</li> |
|
|
723 |
|
|
|
724 |
|
|
|
725 |
</ul> |
|
|
726 |
</div> |
|
|
727 |
</div> |
|
|
728 |
|
|
|
729 |
|
|
|
730 |
|
|
|
731 |
</div> |
|
|
732 |
</div> |
|
|
733 |
</div> |
|
|
734 |
</div> |
|
|
735 |
|
|
|
736 |
<script src="../assets/vendor/prettify/prettify-min.js"></script> |
|
|
737 |
<script>prettyPrint();</script> |
|
|
738 |
|
|
|
739 |
<script> |
|
|
740 |
YUI.Env.Tests = { |
|
|
741 |
examples: [], |
|
|
742 |
project: '../assets', |
|
|
743 |
assets: '../assets/attribute', |
|
|
744 |
name: 'attribute', |
|
|
745 |
title: 'Attribute', |
|
|
746 |
newWindow: '', |
|
|
747 |
auto: false |
|
|
748 |
}; |
|
|
749 |
YUI.Env.Tests.examples.push('attribute-basic'); |
|
|
750 |
YUI.Env.Tests.examples.push('attribute-rw'); |
|
|
751 |
YUI.Env.Tests.examples.push('attribute-event'); |
|
|
752 |
YUI.Env.Tests.examples.push('attribute-basic-speeddate'); |
|
|
753 |
YUI.Env.Tests.examples.push('attribute-event-speeddate'); |
|
|
754 |
YUI.Env.Tests.examples.push('attribute-getset'); |
|
|
755 |
|
|
|
756 |
</script> |
|
|
757 |
<script src="../assets/yui/test-runner.js"></script> |
|
|
758 |
|
|
|
759 |
|
|
|
760 |
|
|
|
761 |
</body> |
|
|
762 |
</html> |