e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai<style type="text/css" scoped>
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai #speeddate h1 {
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai font-size: 108%;
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai margin-bottom:2em;
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai margin-bottom:10px;
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai .sd-nametag {
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai border:1px solid #000;
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai text-align:center;
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai margin:20px;
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai background-color:#00f;
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai border-radius: 10px;
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai -webkit-border-radius: 10px;
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai -moz-border-radius: 10px;
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 .sd-nametag .sd-hd, .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 .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 .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 .sd-nametag .sd-bd {
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai background-color:#fff;
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai padding:1em;
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai .sd-nametag .sd-bd .sd-name,
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai .sd-nametag .sd-bd .sd-personality {
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai font-size:108%;
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai font-weight:900;
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai font-family:monospace;
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai text-decoration:underline;
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai .sd-nametag .sd-bd .sd-personality.sd-max {
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai<div class="intro">
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai This example builds on the <a href="attribute-basic.html">"Basic Configuration" example</a>,
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai showing how you can use attribute to model objects in your application.
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai As with the basic example, it is geared towards users who want to create their own classes from
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai scratch and add attribute support. In most cases you should consider extending the
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai <a href="../base/index.html">`Base`</a> class when you need attribute support, instead
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai of augmenting Attribute directly. <a href="../base/index.html">`Base`</a> does the work described
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai in this example for you, in addition to making it easier for users to extend you class.
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai<div class="example">
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai {{>attribute-basic-speeddate-source}}
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai<h2>Setting Up a SpeedDater Class</h2>
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai<p>In this example, we'll create a custom `SpeedDater` class, and show how you can use attributes to manage the state for a Speed Dater.
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen DesaiIn the <a href="attribute-event-speeddate.html">"Attribute Event Based Speed Dating" example</a> we'll modify this example to leverage attribute change events.</p>
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai<h3>Creating A YUI Instance</h3>
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai<p>As with the other attribute <a href="attribute-basic.html">examples</a>, we'll setup our own instance of the YUI object and request the modules we require using the code pattern shown below:</p>
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai // Create our local YUI instance, to avoid
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai // modifying the global YUI object
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai YUI({...}).use("attribute", "node", ... function(Y) {
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai // Example code is written inside this function,
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai // which gets passed our own YUI instance, Y, populated
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai // with the modules we asked for (e.g. Y.Attribute, Y.Node etc.)
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai<h3>Defining The SpeedDater Class</h3>
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai<p>The first step in the example is to create the constructor function for our new class, to which we want to add attribute support. In our example, this class is called `SpeedDater`.
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen DesaiWe then augment `SpeedDater` with `Y.Attribute`, so that it receives all of `Attribute's` methods, in addition to any it may defined itself:</p>
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai// Setup custom class which we want to add attribute support to
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desaifunction SpeedDater(cfg) {
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai// Augment custom class with Attribute
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai<h3>Adding Attributes</h3>
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen DesaiWe can now set up any attributes we need for `SpeedDater` using Attribute's `addAttrs()` method.
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen DesaiFor this example we add 3 attributes - `name`, `personality`, and `available`.
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen DesaiWe provide an default initial `value` for `personality` and `available`, but don't have anything for `name`.
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen DesaiAs mentioned in the basic example, the same object literal we use to provide the initial value for the attribute can also be used to configure attribute properties such as `readOnly` or
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai`writeOnce`, and to define `getter`, `setter` and `validator` methods for the attribute. For `name`, we configure it to be `writeOnce`,
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desaimeaning that it's value can be set once by the user, but not modified after that single set.
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen DesaiThe default set of attributes which `SpeedDater` will support is passed to `addAttrs` to set up the attributes for each instance during construction.
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen DesaiAs mentioned previously, if you expect your class to be extended, <a href="../base/index.html">Base</a> provides a more convenient way for you to define the same attribute configuration statically for your class, so that it can be modified by extended classes.
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen DesaiBase will take care of isolating the static configuration, so that it isn't modified across instances.
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen DesaiThe complete definition for `SpeedDater` is shown below:</p>
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai// Setup custom class which we want to
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai// add managed attribute support to
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desaifunction SpeedDater(cfg) {
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai // When constructed, setup the initial attributes for the
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai // instance, by calling the addAttrs method.
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai var attrs = {
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai // Add 3 attributes: name, personality, available
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai writeOnce:true
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai personality : {
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai available : {
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen DesaiSpeedDater.prototype.applyNameTag = function(where) {
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai // Method used to render the visual representation of a
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai // SpeedDater object's state (in this case as a name tag)
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen DesaiSpeedDater.prototype.updateNameTag = function() {
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai // Method used to update the rendered state of SpeedDater in the DOM.
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai// Template to use form the markup
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen DesaiSpeedDater.NAMETAG = "<div class="sd-nametag"><div class="sd-hd">Hello!</div>... </div>";
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai// Augment custom class with Attribute
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen DesaiThe `addAttrs()` method, in addition to the default attribute configuration, also accepts an object literal (associative array) of name/value pairs which can be
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desaiused to over-ride the default initial values of the attributes. This is useful for classes which wish to allow the user the set the value of attributes as part of object
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desaiconstruction, as shown by the use of the `cfg` argument above.
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai<h3>Using Attributes</h3>
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai<p>Now that we have `SpeedDater` defined with the set of attributes it supports, we can create multiple instances of `SpeedDater` defining the initial
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desaiattribute state for each instance through the constructor. We can also update the instance's attribute state after construction, using the `get` and
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai`set` methods defined by Attribute.</p>
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai<p>We create a first instance, `john`, setting up the intial state using Attribute's constructor configuration object support:</p>
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai// Set both name and personality during construction
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desaijohn = new SpeedDater({
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai name: "John",
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai personality: 76.43
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai<p>For the second instance that we create, `jane`, we set the value of the personality attribute, after construction:</p>
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai// Set name during construction
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desaijane = new SpeedDater({
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai name: "Jane"
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai// Set personality after construction. The initial value for personality
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai// in this case, will be the value defined when the attribute was added
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai// using addAttrs (above)
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desaijane.set("personality", 82);
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai<p>We render the current attribute state of each instance to the DOM, using the `applyNameTag()` method defined on SpeedDater's prototype:</p>
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai// Render the sticker with john's state information to the DOM
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai// Render the sticker with jane's state information to the DOM
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desaijane.applySicker("#jane .shirt");
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai<p>Although not directly related to working with Attributes, it's worth taking a look at the `applyNameTag()` and `updateNameTag()` implementations, since they establish
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desaia commonly used pattern.</p>
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai<p>The `applyNameTag()` method handles rendering the initial visual representation for a speed dater object's state (in this case a name tag). It uses tokens in an HTML "template" string, which it replaces with the values
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desaiof attributes using the `substitute()` utility method:</p>
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai// A template for the markup representing the SpeedDater object..
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen DesaiSpeedDater.NAMETAG = '<div class="sd-nametag"> \
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai <div class="sd-hd">Hello!</div> \
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai <div class="sd-bd">I\'m <span class="sd-name">{name}</span> \
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai and my PersonalityQuotientIndex is \
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai <span class="sd-personality">{personality}</span> \
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai <div class="sd-ft sd-availability">{available}</div> \
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai// A rendering method, used to create the initial markup for the SpeedDater.
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen DesaiSpeedDater.prototype.applyNameTag = function(where) {
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai // This example uses an HTML template string containing placeholder
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai // tokens (SpeedDater.NAMETAG above), and Y.substitute to replace the
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai // tokens with the current attribute values.
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai var tokens = {
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai // Get attribute values and map them to the tokens in the HTML template string
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai name: this.get("name"),
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai available: (this.get("available")) ? "I'm still looking " : "Sorry, I'm taken",
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai personality: this.get("personality")
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai // Create a new Node element, from the token substituted string...
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai this.nameTag = Y.Node.create(Y.substitute(SpeedDater.NAMETAG, tokens));
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai // ... and append it to the DOM
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai<p>The `updateNameTag()` method handles updating this visual representation with the current state, when requested by the user</p>
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai// An update method, used to refresh the rendered content, after
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai// an attribute value is changed.
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen DesaiSpeedDater.prototype.updateNameTag = function() {
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai // Get current attribute values...
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai var taken = (this.get("available")) ? "I'm still looking " : "Sorry, I'm taken";
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai var name = this.get("name");
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai var personality = this.get("personality");
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai // Find the corresponding element, and replace the innerHTML with the new value
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai this.nameTag.one(".sd-name").set("innerHTML", name);
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai this.nameTag.one(".sd-availability").set("innerHTML", taken);
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai var personalityEl = this.nameTag.one(".sd-personality");
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai personalityEl.set("innerHTML", personality);
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai if (personality > 90) {
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai<p>Each instance's state can be now be updated using Attribute's `set` method, and the subsequent call to SpeedDater's `updateNameTag()` method will update the visual representation (the rendered DOM) of the object:</p>
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen DesaiY.on("click", function() {
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai john.set("available", false);
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai}, "#john .taken");
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen DesaiY.on("click", function() {
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai jane.set("available", false);
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai}, "#jane .taken");
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen DesaiY.on("click", function() {
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai jane.set("personality", 98);
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai}, "#jane .upgrade");
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai<p>In the <a href="attribute-event-speeddate.html">"Attribute Event Based Speed Dating" example</a>, we'll see how we can use Attribute change events to eliminate the need for users to call `updateNameTag()` each time they set an attribute, and have the two instances communicate with one another.</p>
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai<h2>Complete Example Source</h2>
e808b8824ca1091c8efb5669db9129e68e5e1c14Satyen Desai{{>attribute-basic-speeddate-source}}