<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Yahoo! User Interface Blog (YUIBlog) &#187; jafl</title>
	<atom:link href="http://www.yuiblog.com/blog/index.php/tag/jafl/feed/" rel="self" type="application/rss+xml" />
	<link>http://www.yuiblog.com/blog</link>
	<description>The official blog of the YUI Project.</description>
	<lastBuildDate>Tue, 14 Feb 2012 00:42:26 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.0.4</generator>
		<item>
		<title>In the YUI 3 Gallery:  Bulk Editor Widget</title>
		<link>http://www.yuiblog.com/blog/2011/12/05/bulk-edi/</link>
		<comments>http://www.yuiblog.com/blog/2011/12/05/bulk-edi/#comments</comments>
		<pubDate>Mon, 05 Dec 2011 21:01:38 +0000</pubDate>
		<dc:creator>John Lindal</dc:creator>
				<category><![CDATA[Development]]></category>
		<category><![CDATA[YUI 3 Gallery]]></category>
		<category><![CDATA[inline editing]]></category>
		<category><![CDATA[jafl]]></category>

		<guid isPermaLink="false">http://www.yuiblog.com/blog/2011/12/05/</guid>
		<description><![CDATA[The QuickEdit plugin for YUI 3 DataTable makes it easy to edit an entire page of records as an atomic operation. However, sometimes you need to do even more. For example, you might have to simultaneously edit more records than you can comfortably fit on a single page. Or you might need to support adding, [...]]]></description>
			<content:encoded><![CDATA[<p>The <a href="http://www.yuiblog.com/blog/2011/04/19/quickedit-datatable-yui3/">QuickEdit plugin</a> for <a href="http://developer.yahoo.com/yui/3/datatable/">YUI 3 DataTable</a> makes it easy to edit an entire page of records as an atomic operation.  However, sometimes you need to do even more.  For example, you might have to simultaneously edit more records than you can comfortably fit on a single page.  Or you might need to support adding, duplicating, and removing records as part of the atomic operation.  Or you might wish to visually group fields by placing them in a single table cell.  The <a href="http://yuilibrary.com/gallery/show/bulkedit">Bulk Editor</a> widget supports all these possibilities.</p>
<p><a href="http://jafl.github.com/yui3-gallery/bulkedit/" title="YUI 3 Bulk Editor Screenshot"><img src="http://jafl.github.com/yui3-gallery/bulkedit/example.gif"></a></p>
<p>(<a href="http://jafl.github.com/yui3-gallery/bulkedit/" title="YUI 3 Bulk Editor Example">Click the screenshot to play with this example</a>.)</p>
<h3>Overview</h3>
<p>The Bulk Editor widget consists of three components:</p>
<dl>
<dt><code>Data source</code></dt>
<dd>
<p style="margin:0">This wraps a YUI DataSource and manages the changes: insertions, removals, and changed values.</p>
</dd>
<dt><code>Base widget</code></dt>
<dd>
<p style="margin:0">This provides the basic structure for editing by managing records and fields within each record.  Derived classes are responsible for rendering each record into a separate <q>row</q>, which could be a div, a tbody, or some other container.</p>
</dd>
<dt><code>HTML table implementation</code></dt>
<dd>
<p style="margin:0">This extends the base widget to render each record into a tbody in an HTML table.  The column configuration determines which field is displayed in each column of the table.  A custom cell formatter can be used to render multiple fields in a single table cell.</p>
</dd>
</dl>
<h3>Configuration</h3>
<p>In the example that generated the above screenshot, the configuration has been kept as simple as possible:</p>
<p><code>fields</code> defines the editable values in each record.  The default type is <q>input</q>.  The other valid types are <q>select</q> and <q>textarea</q>.  (<q>select</q> requires a list of values.)  Basic validation is provided by <a href="http://yuilibrary.com/gallery/show/formmgr-css-validation">Form Manager</a> gallery module.  This covers <a href="http://jafl.github.com/yui3-gallery/yuidoc/module_gallery-formmgr-css-validation.html">required fields, length restrictions, and numeric ranges</a>.  More complex validation can be performed by specifying <code>regex</code> or your own function (<code>fn</code>).  Here is an excerpt from the live example:</p>
<pre>
var fields =
{
	title:
	{
		type: 'textarea'
	},
	year:
	{
		validation:
		{
			css: 'yiv-integer:[1500,2100]'
		}
	},
	color:
	{
		type: 'select',
		values:
		[
			{ value: 'red',   text: 'Red'   },
			{ value: 'green', text: 'Green' },
			{ value: 'blue',  text: 'Blue'  }
		]
	}
};
</pre>
<p><code>Y.BulkEditDataSource</code> requires an instance of <code>Y.DataSource</code> and the following parameters:</p>
<dl>
<dt><code>uniqueIdKey</code></dt>
<dd>
<p style="margin:0">The name of a key which uniquely identifies each record.</p>
</dd>
<dt><code>generateRequest</code></dt>
<dd>
<p style="margin:0">A function to generate request parameters for <code>Y.DataSource</code>.  (This is empty in the example, because <code>Y.DataSource.Local</code> always returns all the data.)</p>
</dd>
<dt><code>extractTotalRecords</code></dt>
<dd>
<p style="margin:0">A function to extract the total number of records from the <code>Y.DataSource</code> response.</p>
</dd>
</dl>
<p>Since the example uses <code>Y.DataSource.Local</code>, <code>totalRecordsReturnExpr</code> is also required.  This OGNL expression specifies where in the response to store the total number of records.  (Notice that <code>extractTotalRecords</code> reads this value.)</p>
<pre>
var ds = new Y.BulkEditDataSource(
{
	ds:                     raw_ds,
	uniqueIdKey:            'id',
	generateRequest:        function() { },
	totalRecordsReturnExpr: '.meta.totalRecords',
	extractTotalRecords:    function(response)
	{
		return response.meta.totalRecords;
	}
});
</pre>
<p><code>Y.HTMLTableBulkEditor</code> requires the data source, the field configuration, and the column configuration.  In the column configuration, the key is the field name, unless you specify a custom formatter.  The label is used as the column title.  Here is an excerpt from the live example:</p>
<pre>
var columns =
[
	{
		key:       'checkbox',
		label:     '&lt;input type="checkbox" id="select-all" /&gt;',
		formatter: function(o)
		{
			var markup = '&lt;input type="checkbox" class="record-select" id="{id}" /&gt;';
			o.cell.set('innerHTML', Y.Lang.sub(markup,
			{
				id: this.getRecordId(o.record)
			}));
		}
	},
	{ key: 'title', label: 'Title' },
	{ key: 'year', label: 'Year' },
	{ key: 'color', label: 'Color' }
];
</pre>
<p>(Note that the live example defines a minor extension to <code>Y.HTMLTableBulkEditor</code> to handle the checkbox column.)</p>
<p>You can also pass an instance of <a href="http://yuilibrary.com/gallery/show/paginator"><code>Y.Paginator</code></a> to <code>Y.BulkEditDataSource</code>.  This is demonstrated in a separate, more complicated <a href="http://jafl.github.com/yui3-gallery/bulkedit/complex.html">live example</a>.</p>
<h3>Local vs. Remote Data Sources</h3>
<p>When deciding whether to use a local or a remote datasource, you must carefully consider the trade-offs.  The obvious trade-off is that a local datasource is faster when paginating, but the initial page load will take longer, and it requires more memory on the client.</p>
<p>The Bulk Editor widget imposes additional trade-offs, however.</p>
<p>First, the YUI DataSource must return immutable data.  This is automatic for local data sources, but can be tricky to implement for remote data sources.  You will need to lock the rows in your database table for the duration of the bulk edit operation if more than one user is allowed to modify them.</p>
<p>Second, the choice between local and remote data source affects how you are allowed to save the data.  When you use a local data source, you can do <q>best effort</q> saving, i.e., save all the valid records to the server, remove them from the local datasource, and allow the user to focus on the records that have invalid values.  When you use a remote data source, the immutability requirement only allows you to do <q>all or nothing</q> saving, i.e., the data can only be saved after all the data is valid.</p>
<h3>Real-world Use Case</h3>
<p>The original motivation for the Bulk Editor widget was to allow post-processing of an uploaded spreadsheet.  Introducing a post-processing step removes the need for the spreadsheet values to be perfect.  Errors can be flagged and fixed in the Bulk Editor widget instead of rejecting the entire upload.  In addition, processing on the server can do best-guess assignment of additional values required for each record, and the user can check and fix these extra values before saving.  This simplifies the initial creation of the spreadsheet.</p>
<p>In this scenario, a remote data source is the best choice.  The uploaded data is stored in a scratch space, and is therefore guaranteed immutable, since no other user can see it.  &#8220;All or nothing&#8221; saving is appropriate:  Once all the errors have been fixed, the save operation is atomic, just like a standard upload operation.</p>
<p class="authorbox"><em><strong>About the author:</strong> <a href="http://jjlindal.net/jafl/blog/">John Lindal</a> (<a href="http://twitter.com/jafl5272/">@jafl5272</a> on Twitter) is one of the lead engineers constructing the foundation on which <a href="http://apt.yahoo.com/">Yahoo! APT</a> is built.  Previously, he worked on the Yahoo! Publisher Network.</em></p>
]]></content:encoded>
			<wfw:commentRss>http://www.yuiblog.com/blog/2011/12/05/bulk-edi/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Quick Edit mode for YUI 3 DataTable</title>
		<link>http://www.yuiblog.com/blog/2011/04/19/quickedit-datatable-yui3/</link>
		<comments>http://www.yuiblog.com/blog/2011/04/19/quickedit-datatable-yui3/#comments</comments>
		<pubDate>Tue, 19 Apr 2011 22:20:32 +0000</pubDate>
		<dc:creator>John Lindal</dc:creator>
				<category><![CDATA[Development]]></category>
		<category><![CDATA[YUI 3 Gallery]]></category>
		<category><![CDATA[datatable]]></category>
		<category><![CDATA[inline editing]]></category>
		<category><![CDATA[jafl]]></category>
		<category><![CDATA[YUI 3]]></category>

		<guid isPermaLink="false">http://www.yuiblog.com/blog/2011/04/15/</guid>
		<description><![CDATA[Even though YUI 3 DataTable does not yet have inline editing of individual cells, it is relatively simple to implement Quick Edit mode. The QuickEdit plugin for DataTable in the YUI 3 Gallery allows all the visible values in a DataTable to be edited simultaneously. (Click the screenshot to play with this example.) Overview As [...]]]></description>
			<content:encoded><![CDATA[<p>Even though <a href="http://developer.yahoo.com/yui/3/datatable/">YUI 3 DataTable</a> does not yet have inline editing of individual cells, it is relatively simple to implement Quick Edit mode.  The <a href="http://yuilibrary.com/gallery/show/quickedit">QuickEdit</a> plugin for DataTable in the <a href="http://yuilibrary.com/gallery/">YUI 3 Gallery</a> allows all the visible values in a DataTable to be edited simultaneously.</p>
<p><a href="http://jafl.github.com/yui3-gallery/quickedit/" title="YUI 3 Quick Edit Screenshot"><img src="http://jafl.github.com/yui3-gallery/quickedit/example.png"></a></p>
<p>(<a href="http://jafl.github.com/yui3-gallery/quickedit/" title="YUI 3 Quick Edit Example">Click the screenshot to play with this example</a>.)</p>
<h3>Overview</h3>
<p>As with the <a href="http://www.yuiblog.com/blog/2010/08/19/quickedit-datatable/">YUI 2 version</a>, the core idea of Quick Edit mode is to swap out all the cell formatters with new ones which populate the cells with form elements, e.g., input fields or dropdowns.  This is done when <code>start()</code> is called, based on the configuration described below.  After the user is finished, you can call <code>getChanges()</code> to get the changed values and then persist them.  To exit Quick Edit mode, call <code>cancel()</code>.  (It is named <q>cancel</q> instead of <q>stop</q> to remind you that it <em>discards</em> all changes.)</p>
<p>Since the Quick Edit gallery module is a plugin for DataTable, you need to plug it in to your datatable before you can use it:</p>
<pre>
my_table.plug(Y.Plugin.DataTableQuickEdit);
</pre>
<p>This stores the plugin in the <code>qe</code> member of the datatable, so you must call the plugin&#8217;s functions like this:</p>
<pre>
my_table.qe.start();
</pre>
<h3>Configuration</h3>
<p>Quick Edit adds two new configuration attributes to all columns: <code>quickEdit</code> and <code>qeFormatter</code>.</p>
<p>If a column&#8217;s <code>quickEdit</code> property is defined, the column will be editable in Quick Edit mode.  To accept all the defaults, you can simply set <code>quickEdit:true</code>.  For more control, you can pass an object with the following properties:</p>
<dl>
<dt><code>formatter</code></dt>
<dd>
<p style="margin:0">The cell formatter which will render an appropriate form field: &lt;input type=&#8221;text&#8221;&gt;, &lt;textarea&gt;, or &lt;select&gt;.  By default, the cell formatter <code>Y.Plugin.DataTableQuickEdit.textFormatter</code> is used for all cells to produce input elements.  To get a <code>textarea</code> element, configure a column to use <code>Y.Plugin.DataTableQuickEdit.textareaFormatter</code> instead.</p>
</dd>
<dt><code>validation</code></dt>
<dd>
<p style="margin:0">Validation configuration for every field in the column.</p>
<dl>
<dt><code>css</code></dt>
<dd>
<p style="margin:0">CSS classes encoding basic validation rules:</p>
<dl>
<dt><code>yiv-required</code></dt>
<dd>
<p style="margin:0">Value must not be empty.</p>
<dt><code>yiv-length:[x,y]</code></dt>
<dd>
<p style="margin:0">String must be at least <code>x</code> characters and at most <code>y</code> characters. At least one of x and y must be specified.</p>
<dt><code>yiv-integer:[x,y]</code></dt>
<dd>
<p style="margin:0">The integer value must be at least <code>x</code> and at most <code>y</code>. <code>x</code> and <code>y</code> are both optional.</p>
<dt><code>yiv-decimal:[x,y]</code></dt>
<dd>
<p style="margin:0">The decimal value must be at least <code>x</code> and at most <code>y</code>. Exponents are not allowed. <code>x</code> and <code>y</code> are both optional.</p>
</dl>
</dd>
<dt><code>fn</code></dt>
<dd>
<p style="margin:0">A function that will be called with the DataTable as its scope and the cell&#8217;s form element as the argument. Return true if the value is valid. Otherwise, call <code>this.displayMessage(...)</code> to display an error and then return false.</p>
</dd>
<dt><code>msg</code></dt>
<dd>
<p style="margin:0">A map of types to messages that will be displayed when a basic or regex validation rule fails. The valid types are: <code>required</code>, <code>min_length</code>, <code>max_length</code>, <code>integer</code>, <code>decimal</code>, and <code>regex</code>.  There is no default for type <code>regex</code>, so you must specify a message if you configure a regex validation.  The default error messages for the other types are stored in <code>Y.FormManager.Strings</code> (provided by <a href="http://yuilibrary.com/gallery/show/formmgr-css-validation">gallery-formmgr-css-validation</a>) and can be overridden and/or localized.</p>
</dd>
<dt><code>regex</code></dt>
<dd>
<p style="margin:0">Regular expression that the value must satisfy in order to be considered valid.</p>
</dd>
</dl>
</dd>
</dl>
<p>Sometimes, a non-editable column must be rendered differently during Quick Edit mode.  The best example is a column containing a link, since navigating away from the page while in Quick Edit mode can be disastrous.  To remove the link during Quick Edit, configure <code>qeFormatter</code> for the column to be <code>Y.Plugin.DataTableQuickEdit.readonlyLinkFormatter</code>.  For email addresses, use <code>Y.Plugin.DataTableQuickEdit.readonlyEmailFormatter</code>.  You can also write you own custom, read-only formatter.  Simply follow the normal rules for constructing a DataTable cell formatter.</p>
<h3>Missing Features</h3>
<p>Due to a <a href="http://yuilibrary.com/projects/yui3/ticket/2529839">bug in YUI 3.3.0 DataTable</a>, the <code>td</code> element passed to a column formatter is actually from the <em>previous</em> column.  This made it too troublesome to support <q>copy down</q>, where a button in the first row lets you copy the value down to all other rows.</p>
<p>The bug also required a complete reworking of the basic Quick Edit cell formatters to return text instead of manipulating the DOM.  This is why custom cell formatters are not officially supported in this initial release.  If you are adventurous, you can still build them, but keep in mind that you will need to rewrite them, including adding in support for <q>copy down,</q> once the bug in DataTable is fixed.</p>
<p class="authorbox"><em><strong>About the author:</strong> <a href="http://jjlindal.net/jafl/blog/">John Lindal</a> (<a href="http://twitter.com/jafl5272/">@jafl5272</a> on Twitter) is one of the lead engineers constructing the foundation on which <a href="http://apt.yahoo.com/">Yahoo! APT</a> is built.  Previously, he worked on the Yahoo! Publisher Network.</em></p>
]]></content:encoded>
			<wfw:commentRss>http://www.yuiblog.com/blog/2011/04/19/quickedit-datatable-yui3/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Filtering the Data Displayed by YUI 3 DataTable</title>
		<link>http://www.yuiblog.com/blog/2011/03/01/filtering-yui3-datatable/</link>
		<comments>http://www.yuiblog.com/blog/2011/03/01/filtering-yui3-datatable/#comments</comments>
		<pubDate>Tue, 01 Mar 2011 21:39:43 +0000</pubDate>
		<dc:creator>John Lindal</dc:creator>
				<category><![CDATA[Development]]></category>
		<category><![CDATA[YUI 3 Gallery]]></category>
		<category><![CDATA[datatable]]></category>
		<category><![CDATA[filter]]></category>
		<category><![CDATA[jafl]]></category>
		<category><![CDATA[querybuilder]]></category>
		<category><![CDATA[YUI 3]]></category>

		<guid isPermaLink="false">http://www.yuiblog.com/blog/2011/02/22/</guid>
		<description><![CDATA[In addition to sorting, which is supported by YUI 3 DataTable, it is often useful to be able to filter the data and display a subset of the available rows. The Query Builder widget in the YUI 3 Gallery provides a UI for constructing a simple filter expression. (Click the screenshot to play with this [...]]]></description>
			<content:encoded><![CDATA[<p>In addition to sorting, which is supported by <a href="http://developer.yahoo.com/yui/3/datatable/">YUI 3 DataTable</a>, it is often useful to be able to filter the data and display a subset of the available rows.  The <a href="http://yuilibrary.com/gallery/show/querybuilder">Query Builder</a> widget in the <a href="http://yuilibrary.com/gallery/">YUI 3 Gallery</a> provides a UI for constructing a simple filter expression.</p>
<p><a href="http://jafl.github.com/yui3-gallery/querybuilder/"><img src="http://jafl.github.com/yui3-gallery/querybuilder/example.gif"></a></p>
<p>(<a href="http://jafl.github.com/yui3-gallery/querybuilder/" title="YUI 3 Query Builder Example">Click the screenshot to play with this example</a>.)</p>
<h3>History</h3>
<p>The first version was written by a colleague working with me on the Yahoo! Publisher Network (YPN).  (He left soon afterwards to attempt honest employment.  Following the precedent set by Jamie Zawinski, he opened a pub to sell beer &#8212; home brewed, no less!  But I digress&#8230;.)  After hacking together the first version of Query Builder, he made the mistake of showing it to me.  A few days later, he complained, &#8220;You rewrote the whole thing!&#8221;  In fact, I have rewritten it several times over the years.  YPN is gone, but the latest YUI 2 version of Query Builder powers all data tables in <a href="http://apt.yahoo.com/">APT</a>, Yahoo&#8217;s display advertising management platform.  The port to YUI 3 is actually the least amount of work I have had to do to generate a new version!</p>
<h3>How the example works</h3>
<p>The core of the example is (1) the Query Builder configuration which specifies how the user can filter the data and (2) the extension to Y.DataSource.Local which implements the filter.  (For server side pagination, you would send the filter data to the server and bake it into your SQL query.)</p>
<p>To configure Query Builder, the example first defines a list of the variables that can be filtered:</p>
<pre>
var var_list =
[
	{
		name: 'title',
		type: 'string',
		text: 'Title'
	},
	{
		name: 'year',
		type: 'number',
		text: 'Year',
		validation: 'yiv-integer:[0,2100]'
	},
	{
		name: 'quantity',
		type: 'number',
		text: 'Quantity',
		validation: 'yiv-integer:[0,]'
	}
];
</pre>
<p>Each variable is assigned a name (matching the key in the DataTable column configuration) and a type.  The default types are &#8216;string&#8217;, &#8216;number&#8217;, and &#8216;select&#8217;, but you can extend this by building custom plugins (<a href="#plugins">see below</a>).  For each type that you use, you must also define a set of operators:</p>
<pre>
var ops =
{
	string:
	[
		{ value: 'contains',    text: 'Contains' },
		{ value: 'starts-with', text: 'Starts with' },
		{ value: 'ends-with',   text: 'Ends with' }
	],

	number:
	[
		{ value: 'equal',   text: '=' },
		{ value: 'less',    text: '&lt;=' },
		{ value: 'greater', text: '&gt;=' }
	]
};
</pre>
<p>This specifies the operators that the user can apply to each variable type.  (If you need different sets of operators for variables of the same fundamental type, you can clone the type.  See the <a href="#plugins">Plugins</a> section below.)</p>
<p><a href="http://yuilibrary.com/gallery/show/formmgr">Y.FormManager</a> is used to validate the values entered by the user before the filter is applied.  The <q>validation</q> key for each variable in the above Query Builder configuration provides CSS data which is interpreted by Y.FormManager.</p>
<p>If all validations pass, a request is sent to the data source.  The extension to <code>Y.DataSource.Local</code> is quite simple.  It merely filters the data before returning the results:</p>
<pre>
Y.extend(CustomDataSource, Y.DataSource.Local,
{
	_defDataFn: function(e)
	{
		var data = filterData(e.data, e.request.filter);
		var response =
		{
			results: data.slice(e.request.recordOffset,
						e.request.recordOffset + e.request.rowsPerPage),
			meta:
			{
				totalRecords: data.length
			}
		};

		this.fire("response", Y.mix({response: response}, e));
	}
});
</pre>
<p>The <code>filter</code> element of the request is obtained from <a href="http://jafl.github.com/yui3-gallery/querybuilder/yuidoc/QueryBuilder.html#method_toDatabaseQuery"><code>QueryBuilder.toDatabaseQuery()</code></a>, which returns a list of <code>[variable, operator, value]</code> tuples.  Also note that the response must include information on the total number of records, since this changes based on the filter being applied.</p>
<p><code>filterData()</code> simply loops over the tuples from <code>toDatabaseQuery()</code>, applying the filter operators defined in a two level lookup table:</p>
<pre>
var filters =
{
	string:
	{
		contains: function(value, filter)
		{
			return (value.indexOf(filter) &gt;= 0);
		},
		'starts-with': function(value, filter)
		{
			return (value.substr(0, filter.length) == filter);
		},
		'ends-with': function(value, filter)
		{
			return (value.substr(-filter.length) == filter);
		}
	},

	number:
	{
		equal: function(value, filter)
		{
			return (parseInt(value, 10) == parseInt(filter, 10));
		},
		less: function(value, filter)
		{
			return (parseInt(value, 10) &lt;= parseInt(filter, 10));
		},
		greater: function(value, filter)
		{
			return (parseInt(value, 10) &gt;= parseInt(filter, 10));
		}
	}
};
</pre>
<p>After all this, DataTable simply displays what it receives from the data source.</p>
<h3 id="plugins">Plugins</h3>
<p><a href="http://jafl.github.com/yui3-gallery/querybuilder/yuidoc/Types.js.html"><code>Y.QueryBuilder.plugin_mapping</code></a> defines the mapping of type names to actual classes.  You can augment this mapping in two ways:</p>
<ol>
<li>Clone an existing type by defining a new name for the same class.  This allows different sets of operators for different variables of the same fundamental type.</li>
<li>Create a new type by implementing the <a href="http://jafl.github.com/yui3-gallery/querybuilder/yuidoc/QueryBuilder.html">plugin API</a>.  Studying the source code for the existing plugins is the best way to get a feel for how this API works.  </li>
</ol>
<h3>Generalizing Query Builder</h3>
<p>Query Builder does not support parentheses, so you can either AND all the conditions or OR all the conditions.  While it is possible to introducing parentheses into a graphical representation of a Boolean expression, all the designs that I have seen are too cumbersome to use.  A textual representation is much simpler and easier to manipulate.  <a href="http://yuilibrary.com/gallery/show/exprbuilder">Expression Builder</a> incorporates Query Builder into a widget that allows constructing a textual representation without having to remember the syntax or type everything in by hand.</p>
<p class="authorbox"><em><strong>About the author:</strong> <a href="http://jjlindal.net/jafl/blog/">John Lindal</a> (<a href="http://twitter.com/jafl5272/">@jafl5272</a> on Twitter) is one of the lead engineers constructing the foundation on which <a href="http://apt.yahoo.com/">Yahoo! APT</a> is built.  Previously, he worked on the Yahoo! Publisher Network.</em></p>
]]></content:encoded>
			<wfw:commentRss>http://www.yuiblog.com/blog/2011/03/01/filtering-yui3-datatable/feed/</wfw:commentRss>
		<slash:comments>4</slash:comments>
		</item>
		<item>
		<title>Treeble with YUI 3 DataTable</title>
		<link>http://www.yuiblog.com/blog/2011/01/24/treeble-yui3-datatable/</link>
		<comments>http://www.yuiblog.com/blog/2011/01/24/treeble-yui3-datatable/#comments</comments>
		<pubDate>Mon, 24 Jan 2011 18:41:13 +0000</pubDate>
		<dc:creator>John Lindal</dc:creator>
				<category><![CDATA[Development]]></category>
		<category><![CDATA[YUI 3 Gallery]]></category>
		<category><![CDATA[datatable]]></category>
		<category><![CDATA[jafl]]></category>
		<category><![CDATA[treeble]]></category>
		<category><![CDATA[TreeView]]></category>
		<category><![CDATA[YUI 3]]></category>

		<guid isPermaLink="false">http://www.yuiblog.com/blog/2011/01/21/</guid>
		<description><![CDATA[The beta release of DataTable in YUI 3.3.0 gives us a very powerful component to play with. To kick the tires in a useful way, I decided to update my Treeble examples to use DataTable. (Treeble enables displaying hierarchical data in a table.) To my delight, it was a breeze! All the hard work is [...]]]></description>
			<content:encoded><![CDATA[<p>The beta release of <a href="http://developer.yahoo.com/yui/3/datatable/">DataTable</a> in YUI 3.3.0 gives us a very powerful component to play with.  To kick the tires in a useful way, I decided to update my <a href="http://yuilibrary.com/gallery/show/treeble">Treeble</a> examples to use DataTable.  (Treeble enables <a href="http://www.yuiblog.com/blog/2010/04/14/treeble-using-nested-yui-2-datasources-for-row-expansion/">displaying hierarchical data in a table</a>.)</p>
<p>To my delight, it was a breeze!  All the hard work is done in <a href="http://jafl.github.com/yui3-gallery/treeble/yuidoc/TreebleDataSource.html"><code>TreebleDataSource</code></a>, which extends YUI 3 DataSource, so all I had to do was plug it into DataTable by using <code>Y.Plugin.DataTableDataSource</code> and then configure the columns:</p>
<blockquote>
<pre>
var ds = new Y.TreebleDataSource(...),
	pg = new Y.Paginator(...),
	table;

function sendRequest() {
	table.datasource.load({
		request: {
			startIndex:  pg.getStartIndex(),
			resultCount: pg.getRowsPerPage()
		}
	});
}

var cols = [
    { key: 'yui33-hack', label: '' },
    {
        key: 'treeblenub', label: '',
        formatter: Y.Treeble.buildTwistdownFormatter(sendRequest)
    },
    {
        key: 'title', label: 'Title',
        formatter: Y.Treeble.treeValueFormatter
    },
    ...
];

table = new Y.DataTable.Base({columnset: cols});
table.plug(Y.Plugin.DataTableDataSource, {datasource: ds});
</pre>
</blockquote>
<p>To see the complete source code, refer to the <a href="http://jafl.github.com/yui3-gallery/treeble/">live example</a>.</p>
<p>The only flies in the ointment are:</p>
<ul>
<li>The <q>yui33-hack</q> column.  Due to a <a href="http://yuilibrary.com/projects/yui3/ticket/2529839">bug in YUI 3.3.0 DataTable</a>, the <code>td</code> element passed to a column formatter is actually from the <em>previous</em> column.  Thus, the first column in the table displays the twistdown, and the second column is empty.</li>
<li>Undefined values in the data are displayed as <code>{value}</code> instead of blanks (<a href="http://yuilibrary.com/projects/yui3/ticket/2529858">bug 2529858</a>).</li>
</ul>
<p>In order to make Treeble easier to use, I have added a Sam skin which styles to the CSS classes written out by the <code>Y.Treeble</code> formatters.</p>
<p>Enjoy!</p>
<p class="authorbox"><em><strong>About the author:</strong> <a href="http://jjlindal.net/jafl/blog/">John Lindal</a> (<a href="http://twitter.com/jafl5272/">@jafl5272</a> on Twitter) is one of the lead engineers constructing the foundation on which <a href="http://apt.yahoo.com/">Yahoo! APT</a> is built.  Previously, he worked on the Yahoo! Publisher Network.</em></p>
]]></content:encoded>
			<wfw:commentRss>http://www.yuiblog.com/blog/2011/01/24/treeble-yui3-datatable/feed/</wfw:commentRss>
		<slash:comments>7</slash:comments>
		</item>
		<item>
		<title>Quick Edit mode for YUI 2 DataTable</title>
		<link>http://www.yuiblog.com/blog/2010/08/19/quickedit-datatable/</link>
		<comments>http://www.yuiblog.com/blog/2010/08/19/quickedit-datatable/#comments</comments>
		<pubDate>Thu, 19 Aug 2010 16:42:03 +0000</pubDate>
		<dc:creator>John Lindal</dc:creator>
				<category><![CDATA[Development]]></category>
		<category><![CDATA[datatable]]></category>
		<category><![CDATA[inline editing]]></category>
		<category><![CDATA[jafl]]></category>
		<category><![CDATA[yui 2]]></category>

		<guid isPermaLink="false">http://www.yuiblog.com/blog/?p=2466</guid>
		<description><![CDATA[YUI 2 DataTable provides slick inline editing. When disableBtns is turned on in the column configuration, editing simple values like strings or numbers feels just like Excel. However, the experience cannot be as responsive as a desktop application because each change typically requires an XHR call to the server to store (or reject!) the new [...]]]></description>
			<content:encoded><![CDATA[<p><a href="http://developer.yahoo.com/yui/datatable/">YUI 2 DataTable</a> provides slick inline editing.  When <code>disableBtns</code> is turned on in the column configuration, editing simple values like strings or numbers feels just like Excel.  However, the experience cannot be as responsive as a desktop application because each change typically requires an XHR call to the server to store (or reject!) the new value.  If the user needs to change many of the displayed values, it can be a slow and frustrating experience.  To solve this, I developed QuickEditDataTable.  This extends DataTable to add <q>Quick Edit</q> mode, which allows all editable values to be changed in one bulk operation:</p>
<p><a href="http://jafl.github.com/yui2/quickedit/"><img src="http://jafl.github.com/yui2/quickedit/example.png"></a></p>
<p>(<a href="http://jafl.github.com/yui2/quickedit/" title="YUI 2 Quick Edit Example">Click the screenshot to play with this example</a>.)</p>
<h3>Overview</h3>
<p>To enter Quick Edit mode, call <code>startQuickEdit()</code>.  To exit Quick Edit mode, call <code>cancelQuickEdit()</code>.</p>
<p>It is your responsibility to save the changes before calling <code>cancelQuickEdit()</code>.  To simplify this task, QuickEditDataTable provides <code>getQuickEditChanges()</code>.  This returns an array of objects, one for each row.  Each object contains only the values that were changed in that row, keyed off the column id&#8217;s.  For example, if the table has 4 columns (title, author, year, quantity), and the user only changed the quantity in one row to 20, then the object for that row would be <code>{quantity:20}</code>. The other values would be omitted.</p>
<p>QuickEditDataTable can easily extend <code>YAHOO.widget.ScrollingDataTable</code> if you need that functionality.  If you need this, simply make a copy of the source file and change the base class.</p>
<h3>Configuration</h3>
<p>Quick Edit is a modal state in which the cell formatters for editable columns are swapped out and replaced with special formatters that generate <code>input</code>, <code>textarea</code>, or <code>select</code> elements.  Only columns that have <code>quickEdit</code> configuration will be editable.  The configuration options are:</p>
<dl>
<dt><code>copyDown</code></dt>
<dd>
<p style="margin:0">If true, the top cell in the column will have a button to copy the value down to the rest of the rows.</p>
</dd>
<dt><code>formatter</code></dt>
<dd>
<p style="margin:0">The cell formatter which will render an appropriate form field: &lt;input type=&#8221;text&#8221;&gt;, &lt;textarea&gt;, or &lt;select&gt;.  By default, the cell formatter <code>YAHOO.widget.QuickEditDataTable.textQuickEditFormatter</code> is used for all cells to produce input elements.  To get a <code>textarea</code> element, configure a column to use <code>YAHOO.widget.QuickEditDataTable.textareaQuickEditFormatter</code> instead.  You can also write a custom quick edit formatter &#8212; see below.</p>
</dd>
<dt><code>validation</code></dt>
<dd>
<p style="margin:0">Validation configuration for every field in the column.</p>
<dl>
<dt><code>css</code></dt>
<dd>
<p style="margin:0">CSS classes encoding basic validation rules:</p>
<dl>
<dt><code>yiv-required</code></dt>
<dd>
<p style="margin:0">Value must not be empty.</p>
<dt><code>yiv-length:[x,y]</code></dt>
<dd>
<p style="margin:0">String must be at least <code>x</code> characters and at most <code>y</code> characters. At least one of x and y must be specified.</p>
<dt><code>yiv-integer:[x,y]</code></dt>
<dd>
<p style="margin:0">The integer value must be at least <code>x</code> and at most <code>y</code>. <code>x</code> and <code>y</code> are both optional.</p>
<dt><code>yiv-decimal:[x,y]</code></dt>
<dd>
<p style="margin:0">The decimal value must be at least <code>x</code> and at most <code>y</code>. Exponents are not allowed. <code>x</code> and <code>y</code> are both optional.</p>
</dl>
</dd>
<dt><code>fn</code></dt>
<dd>
<p style="margin:0">A function that will be called with the DataTable as its scope and the cell&#8217;s form element as the argument. Return true if the value is valid. Otherwise, call <code>this.displayQuickEditMessage(...)</code> to display an error and then return false.</p>
</dd>
<dt><code>msg</code></dt>
<dd>
<p style="margin:0">A map of types to messages that will be displayed when a basic or regex validation rule fails. The valid types are: <code>required</code>, <code>min_length</code>, <code>max_length</code>, <code>integer</code>, <code>decimal</code>, and <code>regex</code>.  There is no default for type <code>regex</code>, so you must specify a message if you configure a regex validation.  The default error messages for the other types are stored in <code>YAHOO.widget.QuickEditDataTable.Strings</code> and can be overridden and/or localized.</p>
</dd>
<dt><code>regex</code></dt>
<dd>
<p style="margin:0">Regular expression that the value must satisfy in order to be considered valid.</p>
</dd>
</dl>
</dd>
</dl>
<p>Sometimes, a non-editable column must be rendered differently during Quick Edit mode.  The best example is a column containing a link, since navigating away from the page while in Quick Edit mode can be disastrous.  To remove the link during Quick Edit, configure <code>qeFormatter</code> for the column to be <code>YAHOO.widget.QuickEditDataTable.readonlyLinkQuickEditFormatter</code>.  For email addresses, use <code>YAHOO.widget.QuickEditDataTable.readonlyEmailQuickEditFormatter</code>.  You can also write you own custom, read-only formatter.  Simply follow the normal rules for constructing a DataTable cell formatter.</p>
<h3>Custom Quick Edit Formatters</h3>
<p>To write a custom cell formatter for QuickEdit mode, you must structure the function as follows:</p>
<pre>
function myQuickEditFormatter(el, oRecord, oColumn, oData) {
  var markup =
    '&lt;input type="text" class="{yiv} yui-quick-edit yui-quick-edit-key:{key}"/&gt;' +
    YAHOO.widget.QuickEditDataTable.MARKUP_QE_ERROR_DISPLAY;
    el.innerHTML = lang.substitute(markup, {
      key: oColumn.key,
      yiv: oColumn.quickEdit.validation ? (oColumn.quickEdit.validation.css || '') : ''
    });
    el.firstChild.value = extractMyEditableValue(oData);
    YAHOO.widget.QuickEditDataTable.copyDownFormatter.apply(this, arguments);
};
</pre>
<p>You can use <code>textarea</code> or <code>select</code> instead of <code>input</code>, but you can only create a single field.</p>
<p><code>extractMyEditableValue()</code> does not have to be a separate function nor must it be limited to using only <code>oData</code>. The work should normally be done inline in the formatter function, but the name of the sample function makes the point clear.</p>
<p class="authorbox"><em><strong>About the author:</strong> <a href="http://jjlindal.net/jafl/blog/">John Lindal</a> (<a href="http://twitter.com/jafl5272/">@jafl5272</a> on Twitter) is one of the lead engineers constructing the foundation on which <a href="http://apt.yahoo.com/">Yahoo! APT</a> is built.  Previously, he worked on the Yahoo! Publisher Network.</em></p>
]]></content:encoded>
			<wfw:commentRss>http://www.yuiblog.com/blog/2010/08/19/quickedit-datatable/feed/</wfw:commentRss>
		<slash:comments>9</slash:comments>
		</item>
		<item>
		<title>Treeble:  Using Nested YUI 2 DataSources for Row Expansion</title>
		<link>http://www.yuiblog.com/blog/2010/04/14/treeble-using-nested-yui-2-datasources-for-row-expansion/</link>
		<comments>http://www.yuiblog.com/blog/2010/04/14/treeble-using-nested-yui-2-datasources-for-row-expansion/#comments</comments>
		<pubDate>Wed, 14 Apr 2010 16:00:28 +0000</pubDate>
		<dc:creator>John Lindal</dc:creator>
				<category><![CDATA[Development]]></category>
		<category><![CDATA[datatable]]></category>
		<category><![CDATA[jafl]]></category>
		<category><![CDATA[treeble]]></category>
		<category><![CDATA[TreeView]]></category>
		<category><![CDATA[yui 2]]></category>

		<guid isPermaLink="false">http://www.yuiblog.com/blog/?p=1932</guid>
		<description><![CDATA[John Lindal (@jafl5272 on twitter) is one of the lead engineers constructing the foundation on which Yahoo! APT is built. Previously, he worked on the Yahoo! Publisher Network. Daniel Barreiro&#8217;s recent post about nested tables reminded me that it was about time I finished my &#8220;treeble&#8221; widget. &#8220;Treeble&#8221; comes from merging &#8220;tree&#8221; and &#8220;table.&#8221; The [...]]]></description>
			<content:encoded><![CDATA[<div class="interview">
<div class="intro">
<p><em><a href="http://jjlindal.net/jafl/blog/">John Lindal</a> (<a href="http://twitter.com/jafl5272/">@jafl5272</a> on twitter) is one of the lead engineers constructing the foundation on which <a href="http://apt.yahoo.com/">Yahoo! APT</a> is built.  Previously, he worked on the Yahoo! Publisher Network.</em></p>
</div>
</div>
<p>Daniel Barreiro&#8217;s recent post about nested tables reminded me that it was about time I finished my &#8220;treeble&#8221; widget.  &#8220;Treeble&#8221; comes from merging &#8220;tree&#8221; and &#8220;table.&#8221;  The original motivation was to enable drilling into the details behind each row in a table, e.g., start with a table which displays sales figures for each continent and then drill into each country, region, and city.  Of course, once I started building it, the natural design was to support any hierarchy of data.  As an example, it can display a tree like this:</p>
<p><a href="http://jafl.github.com/yui2/treeble/"><img src="http://jafl.github.com/yui2/treeble/example_all.png"></a></p>
<p>(Click the screenshot to play with this example.)</p>
<h3>Overview</h3>
<p>The treeble widget consists of two parts.  The first is a small extension to YUI DataTable for opening/closing nodes in the tree.  (This is done by augmenting DataTable in order to allow it to work transparently with existing extensions to DataTable, most notably ScrollingDataTable.)  The second part of treeble is a significant extension to YUI DataSource, called TreebleDataSource, which merges the results from simple, flat data sources.  The tree is built dynamically, so you don&#8217;t have to load all your data at once.</p>
<p>The tricky part is paginating the tree:  knowing the subset of nodes which have been opened, request only the visible items.  TreebleDataSource provides two options:</p>
<ol>
<li>Paginate top-level nodes only, so all their children can be visible on the same page (the default)</li>
<li>Paginate so a fixed number of nodes are visible</li>
</ol>
<p>#1 is ideal when the tree is shallow and there are only a few children per node.  #2 is necessary when there may be a very large number of children per node.  The configuration option that controls this behavior is <code>paginateChildren</code>.  <strong>In order for pagination to work, the total number of items available from each simple data source must not change.</strong></p>
<p>Since all the items from all the simple data sources are displayed in a single table with a single set of columns, <strong>the schema given to DataTable must be the union of all the flat data source schemas</strong>.  For example, in the above screenshot, DataTable must have a column for <q>Quantity</q> even though it is only defined for the leaf nodes.</p>
<p>Obviously, rows in the table no longer map directly to records in the simple data sources.  Instead, each record in the DataTable has three special members:</p>
<dl>
<dt><code>_yui_node_ds</code></dt>
<dd>
<p style="margin:0">The DataSource from which the record was retrieved.  If you allow inline editing, this tells you which DataSource to use when saving the new value.  (You can edit the Quantity column in the <a href="http://jafl.github.com/yui2/treeble/">live example</a>.)</p>
</dd>
<dt><code>_yui_node_depth</code></dt>
<dd>
<p style="margin:0">The depth of the node in the tree.  Top-level nodes have depth zero.  This is useful when indenting child nodes, as in the above screenshot.</p>
</dd>
<dt><code>_yui_node_path</code></dt>
<dd>
<p style="margin:0">Array of node indices leading to the record.  For example, <code>[2,5,1]</code> translates to <q>second child of the sixth child of the third top-level node.</q>  <code>DataTable.rowIsOpen()</code> and <code>DataTable.toggleRow()</code> require this array to identify the node.</p>
</dd>
</dl>
<p>The YUIDoc for TreebleDataSource and the extensions to DataTable is <a href="http://jafl.github.com/yui2/treeble/yuidoc/">here</a>.</p>
<h3>Configuring DataTable</h3>
<p>To work with TreebleDataSource, your YUI DataTable must be configured correctly:</p>
<pre>
var myDataSource = new YAHOO.util.TreebleDataSource(...);
var myDataTable = new YAHOO.widget.DataTable(container, columns, myDataSource,
{
  dynamicData:true,
  generateRequest: YAHOO.widget.DataTable.generateTreebleDataSourceRequest,
  displayAllRecords: <em>the opposite of your TreebleDataSource's paginateChildren value</em>,
  <em>other configuration, e.g., paginator</em>
});
</pre>
<p>The special version of <code>generateRequest</code> is required because TreebleDataSource needs to receive a known data format so it can correctly generate the requests to the individual simple data sources.</p>
<h3>Constructing TreebleDataSource</h3>
<p>When you construct TreebleDataSource, you must pass it an instance of YUI DataSource as <code>oLiveData</code>.  One column in the data schema for this data source must have its parser set to either <code>"datasource"</code> or a custom parsing function which does the same job:  construct a new YUI DataSource from the value in the column.  The default data source parser ignores falsey values and constructs a data source from any truthy value, e.g., an array of objects, an XHR URL, or an object which defines <code>dataType</code> and <code>liveData</code>.  The configuration (including <code>responseSchema</code> and <code>treebleConfig</code>) for the new data source is copied from the parent data source.  Writing a custom datasource parser is discussed later in this post.</p>
<p>TreebleDataSource itself has only one configuration parameter, <code>paginateChildren</code>, which controls the pagination behavior, as discussed earlier.  The majority of the treeble-related configuration is set on the simple data sources via the <code>treebleConfig</code> object.  This allows the configuration to be different for each simple data source, as discussed below in the section on mixed data sources.</p>
<h4>Using local DataSources</h4>
<p>The <a href="http://jafl.github.com/yui2/treeble/">live example</a> demonstrates how to work with local DataSources.  The only required values in <code>treebleConfig</code> are <code>generateRequest</code> and <code>totalRecordsReturnExpr</code>:</p>
<pre>
new LocalDataSource(<em>array</em>,
{
  ...,
  treebleConfig:
  {
    totalRecordsReturnExpr: '.meta.totalRecords',
    generateRequest: function(state, path)
    {
      return state;
    }
  }
});
</pre>
<p>Since LocalDataSource ignores all request parameters, <code>generateRequest</code> could actually return null, but it echoes the state object instead to support extensions which can sort the data.</p>
<p>Setting <code>totalRecordsReturnExpr</code> signals TreebleDataSource that the simple data source will return all its records, not just the requested slice.  The actual value of <code>totalRecordsReturnExpr</code> must be an <a href="http://en.wikipedia.org/wiki/OGNL">OGNL</a> expression specifying where in the <code>oParsedResponse</code> object returned by TreebleDataSource to store the total number of visible nodes, based on which nodes are currently open.  In the example, this is <code>oParsedResponse.meta.totalRecords</code>.  Since DataTable has (and must have!) <code>dynamicData</code> set to <code>true</code>, the live example also overrides <code>DataTable.handleDataReturnPayload</code> to set <code>oPayload.totalRecords=oParsedResponse.meta.totalRecords</code>.  This gives the paginator the information it needs to compute the total number of pages.</p>
<h4>Using XHR DataSources</h4>
<p>An example configuration when using XHR DataSources would be to construct the top-level data source as:</p>
<pre>
new XHRDataSource(<em>url</em>,
{
  ...,
  treebleConfig:
  {
    startIndexExpr: '.meta.startIndex',
    totalRecordsExpr: '.meta.totalRecords',
    generateRequest: function(state, path)
    {
      return 'path='+path.join(',')+
                 '&amp;startIndex='+state.startIndex+
                 '&amp;results='+state.results+
                 '&amp;sort='+state.sort+
                 '&amp;dir='+state.dir;
    }
  }
});
</pre>
<p>In this example, <code>generateRequest</code> returns query args which the server will interpret in order to return the appropriately sorted slice of the children of the node specified by <code>path</code>.</p>
<p>The value of <code>startIndexExpr</code> is an <a href="http://en.wikipedia.org/wiki/OGNL">OGNL</a> expression specifying where in the <code>oParsedResponse</code> object returned by the simple data source the index of the first returned node is stored.  In the example, this is <code>oParsedResponse.meta.startIndex</code>.</p>
<p>The value of <code>totalRecordsExpr</code> is an <a href="http://en.wikipedia.org/wiki/OGNL">OGNL</a> expression specifying where in <code>oParsedResponse</code> the total number of nodes is stored.  In the example, this is <code>oParsedResponse.meta.totalRecords</code>.  <code>totalRecordsExpr</code> also specifies where in the <code>oParsedResponse</code> object returned by TreebleDataSource to store the total number of visible nodes, based on which nodes are currently open.</p>
<p>Since DataTable has (and must have!) <code>dynamicData</code> set to <code>true</code>, you would also have to override <code>DataTable.handleDataReturnPayload</code> to set <code>oPayload.totalRecords=oParsedResponse.meta.totalRecords</code>.  This gives the paginator the information it needs to compute the total number of pages.</p>
<h4>Using Mixed DataSources</h4>
<p>TreebleDataSource does not require that all the simple data sources be the same type.  For example, if you have a large number of top-level nodes, but only a small tree of children for each node, then it makes sense to return the entire tree when a top-level node is opened.  The default data source parser actually handles this automagically if you specify <code>startIndexExpr</code>, <code>totalRecordsExpr</code>, and <code>totalRecordsReturnExpr</code> for the top-level data source!</p>
<p>If you have only a few top-level nodes, but each tree of children is huge, then your top-level data source could use local data, and you could build a custom data source parser which instantiates XHR data sources for the children, setting <code>startIndexExpr</code>, <code>totalRecordsExpr</code>, and <code>generateRequest</code> appropriately.</p>
<p>You are only limited by your ability to comprehend the complexity of the system!</p>
<p>Note that, when using a custom data source parser, you must define <code>childNodesKey</code> in <code>treebleConfig</code> for each simple data source so TreebleDataSource knows the name of the data source column.  (When you use the default parser, this is detected automatically.)</p>
]]></content:encoded>
			<wfw:commentRss>http://www.yuiblog.com/blog/2010/04/14/treeble-using-nested-yui-2-datasources-for-row-expansion/feed/</wfw:commentRss>
		<slash:comments>32</slash:comments>
		</item>
		<item>
		<title>In the YUI 3 Gallery: John Lindal&#8217;s Form Manager</title>
		<link>http://www.yuiblog.com/blog/2010/03/23/gallery-form-manager/</link>
		<comments>http://www.yuiblog.com/blog/2010/03/23/gallery-form-manager/#comments</comments>
		<pubDate>Tue, 23 Mar 2010 19:08:30 +0000</pubDate>
		<dc:creator>John Lindal</dc:creator>
				<category><![CDATA[Development]]></category>
		<category><![CDATA[YUI 3 Gallery]]></category>
		<category><![CDATA[forms]]></category>
		<category><![CDATA[gallery]]></category>
		<category><![CDATA[jafl]]></category>
		<category><![CDATA[validation]]></category>

		<guid isPermaLink="false">http://www.yuiblog.com/blog/?p=1508</guid>
		<description><![CDATA[John Lindal (@jafl5272 on twitter) is one of the lead engineers constructing the foundation on which Yahoo! APT is built. Previously, he worked on the Yahoo! Publisher Network. The first version of the code discussed in this article was written in 2006. It has continued to evolve ever since, and now it&#8217;s been shared as [...]]]></description>
			<content:encoded><![CDATA[<div class="interview">
<div class="intro">
<p><em><a href="http://jjlindal.net/jafl/blog/">John Lindal</a> (<a href="http://twitter.com/jafl5272/">@jafl5272</a> on twitter) is one of the lead engineers constructing the foundation on which <a href="http://apt.yahoo.com/">Yahoo! APT</a> is built.  Previously, he worked on the Yahoo! Publisher Network.</em></p>
<p><em>The first version of the code discussed in this article was written in 2006.  It has continued to evolve ever since, and now it&#8217;s been shared as part of the <a href="http://yuilibrary.com/gallery/" title="YUI Library :: Gallery">YUI 3 Gallery</a> as the <a href="http://yuilibrary.com/gallery/show/formmgr">Form Manager</a> module.</em></p>
</div>
</div>
<p>Forms have been a staple on web sites for a very long time.  In the early days, they were quite simple:  the user entered values and then waited while the server processed the values or spit back errors.  The rise of Web 2.0 has significantly improved the experience, most notably by pre-validating on the client.  This provides immediate feedback and avoids pointless connections to the server.</p>
<p>Pre-validation is merely one aspect of forms, however.  The entire cycle is:</p>
<ol>
<li>pre-populate the form with default values;</li>
<li>pre-validate the input in the browser;</li>
<li>submit the form data to the server synchronously or asynchronously;</li>
<li>display the results returned by the server (success or errors).</li>
</ol>
<p>When combined with <a href="http://developer.yahoo.com/yui/3/io/" title="YUI 3: IO">YUI 3 IO</a>, the YUI 3 Gallery module <a href="http://yuilibrary.com/gallery/show/formmgr">Form Manager</a> supports this full cycle.  You can play with the client-side functionality <a href="http://jafl.github.com/yui3-gallery/formmgr/">here</a>.</p>
<h3>Initialization</h3>
<p>The first step, pre-populating the form with default values, is of course best done by setting values directly in the markup, because this works even when JavaScript is turned off.  However, you can also pass a map of default values, keyed on the names of the form elements, to the Form Manager constructor.   When you call <code>prepareForm()</code>, it merges the default values from the DOM with the default values passed to the constructor, with the constructor values taking precedence.  The result is saved so you can easily reset to these values by calling <code>populateForm()</code>.  You can also modify these stored defaults:  <code>setDefaultValues()</code>, <code>setDefaultValue()</code>, or <code>saveCurrentValuesAsDefault()</code>.  (Note that this is different from the browser&#8217;s native <code>reset</code> function, since that uses only values encoded in the DOM.  Form Manager provides <code>clearForm()</code> as a wrapper for <code>reset</code>.)</p>
<p>Another useful function to call during initialization is <code>initFocus()</code>.  This sets focus to the first element in the form.  If filling out the form is the main reason for visiting the page, this saves the user a click.  Obviously, if you have more than one form on the page, you should only call <code>initFocus()</code> for one of them.</p>
<h3>Pre-validation</h3>
<p>Pre-validating user input is a <a href="http://weblog.raganwald.com/2007/09/you-suck.html">tricky business</a>.  In my experience, the simplest approach is best:  check everything after the user says <q>I&#8217;m done.</q>  This avoids the need to filter the input stream (keyup is easy to catch, but paste is notoriously difficult, and it all leads to very unexpected edge case behaviors) and, more importantly, it doesn&#8217;t interrupt the user&#8217;s flow.  This is why Form Manager provides a single function to validate everything in the form (drum roll, please):  <code>validateForm()</code>.</p>
<p>Unlike other solutions, Form Manager stores most of the validation configuration in the DOM.  To mark a field for validation, apply one or more of the following CSS classes directly to the field:</p>
<dl>
<dt><code>yiv-required</code></dt>
<dd>
<p style="margin:0">Value must not be empty.</p>
</dd>
<dt><code>yiv-length:[x,y]</code></dt>
<dd>
<p style="margin:0">String must be at least x characters and at most y characters.  At least one of x and y must be specified.</dd>
<dt><code>yiv-integer:[x,y]</code></dt>
<dd>
<p style="margin:0">The value must be an integer, and the value must be at least x and at most y.  x and y are both optional.</p>
</dd>
<dt><code>yiv-decimal:[x,y]</code></dt>
<dd>
<p style="margin:0">The value must be a decimal, and the value must be at least x and at most y.  Exponents are not allowed.  x and y are both optional.</p>
</dd>
</dl>
<p>For example, if a field must be filled in, and it only accepts between 6 and 10 characters, the CSS class would be <q><code>yiv-required yiv-length:[6,10]</code></q>.</p>
<p>One nice benefit of encoding validation in CSS classes is that it can be applied in related situations, e.g., when editing dynamically created fields in a table.  (I hope to post an example for <a href="http://developer.yahoo.com/yui/datatable/" title="YUI 2: DataTable">YUI 2 DataTable</a> soon.)  FormManager exposes the static function <a href="http://jafl.github.com/yui3-gallery/formmgr/yuidoc/FormManager.html#method_Y.FormManager.validateFromCSSData"><code>validateFromCSSData()</code></a> so you don&#8217;t have to reinvent the wheel.</p>
<p>If you need to use a regular expression to validate a field, register it by calling <a href="http://jafl.github.com/yui3-gallery/formmgr/yuidoc/FormManager.html#method_setRegex"><code>setRegex()</code></a>.  For anything else, you can attach a function to a field by calling <a href="http://jafl.github.com/yui3-gallery/formmgr/yuidoc/FormManager.html#method_setFunction"><code>setFunction()</code></a>.  If you need to perform checks that encompass multiple fields, you can override <code>postValidateForm()</code> on your instance of <code>Y.FormManager</code>.</p>
<p>One final note:  As the name suggests, <strong>pre-validation is not real validation</strong>.  JavaScript is relatively easy to subvert (or turn off completely), so the server must never trust anything that it receives from the client.  In addition, some checks can only be done on the server, e.g., anything that requires hitting the database.</p>
<h3>Displaying Errors</h3>
<p>Obviously, if either pre-validation on the client or validation on the server fails, then you need to notify the user, ideally by highlighting the fields that need attention.  Form Manager supports this via the function <a href="http://jafl.github.com/yui3-gallery/formmgr/yuidoc/FormManager.html#method_displayMessage"><code>displayMessage()</code></a>.</p>
<p>This function expects certain CSS marker classes on the DOM surrouning each form element or tightly coupled set of form elements.  My favorite layout is:</p>
<pre>
&lt;div class="<strong>formmgr-row</strong>&gt;
  ...element label...
  &lt;span class="<strong>formmgr-message-text</strong>"&gt;&lt;/span&gt;
  ...form element marked with CSS class <strong>formmgr-field</strong>...
&lt;/div&gt;
</pre>
<p>This localizes well, since the label is above the field, and when an error message is displayed, it&#8217;s very clear to which field the error applies.  To see it in action, follow <a href="http://jafl.github.com/yui3-gallery/formmgr/">this link</a> and click the Validate button in the upper left corner of the page.</p>
<p>But that is just my preference.  You can arrange the DOM elements any way you want inside the container marked by <code>formmgr-row</code>, as long as somewhere inside is another container marked by <code>formmgr-message-text</code>, and the field itself is marked by <code>formmgr-field</code>.</p>
<h4>Message Types</h4>
<p>One important point is that <code>displayMessage()</code> requires a message type.  The supported types are stored in <code>Y.FormManager.status_order</code> in order of precedence.  The default is <code>[ 'error', 'warn', 'success', 'info' ]</code>, but you can modify this to suit your needs.  The ordering is important because, if you call <code>displayMessage()</code> with a higher precedence type and the field is already displaying a message with a lower precedence, then the new message will replace the original message.  Similarly, a lower precedence message will be ignored if a higher precedence message is already displayed.  This allows you to toss messages at each field with abandon, secure in the knowledge that errors will override warnings.</p>
<p>When a message is displayed, the container marked with <code>formmgr-row</code> and the field marked with <code>formmgr-field</code> both get an extra CSS class: <code>formmgr-has<em>type</em></code>, where <em>type</em> is the message type.  This allows you to style the message, field, label, etc. in a different way for each message type.  In addition, the fieldset containing the form field also gets the same class.  This can be used to direct the user&#8217;s attention when the form is large.  (If several fields within a fieldset have different types of messages, the highest precedence type is set on the fieldset.)</p>
<h4>Messages</h4>
<p>Form Manager includes a default set of error messages for all the validations that can be encoded in CSS.  These strings are stored in <code>Y.FormManager.Strings</code>, so you can modify and/or localize them.</p>
<p>You can also specify custom messages for individual fields by calling <a href="http://jafl.github.com/yui3-gallery/formmgr/yuidoc/FormManager.html#method_setErrorMessages"><code>setErrorMessages()</code></a>.</p>
<p>Note that there is no default message for a regular expression validator, because anything generic like <q>The value does not match the required pattern.</q> is utterly meaningless to the user.  If you do not set a message for the type <code>regex</code> before setting the regular expression itself, Form Manager will log an error to remind you.</p>
<h3>Submitting the Form</h3>
<p>Regardless of whether you submit the data synchronously (via <code>form.submit()</code>) or asynchronously (via <code>Y.io</code>), you will probably want to disable the form while the data is being processed.  Form Manager automatically finds all buttons inside the &lt;form&gt; element.  If you have additional buttons elsewhere on the page, you can register them by calling <code>registerButton()</code>.  All known buttons will be disabled when you call <code>disableForm()</code>.  (If you use XHR, call <code>enableForm()</code> after you receive the response from the server!)</p>
<p>If you submit the form synchronously, then you will serve the same page again if there are errors.  In order to work without JavaScript, you should write the errors directly into the DOM, the same way that Form Manager does it.</p>
<p>If you submit via XHR, then you know that JavaScript is enabled, so you can use <code>displayMessage()</code> to highlight the values which the server rejected.  Obviously, this requires that the response from the server include detailed error information!</p>
<p>As a final note, if the form is in an overlay, then you should only close the overlay if the server response with <q>success</q>; i.e., display errors in the overlay, but display a success message somewhere prominent on the main page.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.yuiblog.com/blog/2010/03/23/gallery-form-manager/feed/</wfw:commentRss>
		<slash:comments>5</slash:comments>
		</item>
		<item>
		<title>In the YUI 3 Gallery:  Checkbox Group Behaviors</title>
		<link>http://www.yuiblog.com/blog/2010/03/01/gallery-checkbox-group/</link>
		<comments>http://www.yuiblog.com/blog/2010/03/01/gallery-checkbox-group/#comments</comments>
		<pubDate>Mon, 01 Mar 2010 21:31:09 +0000</pubDate>
		<dc:creator>John Lindal</dc:creator>
				<category><![CDATA[Development]]></category>
		<category><![CDATA[YUI 3 Gallery]]></category>
		<category><![CDATA[checkbox group]]></category>
		<category><![CDATA[forms]]></category>
		<category><![CDATA[gallery]]></category>
		<category><![CDATA[jafl]]></category>

		<guid isPermaLink="false">http://www.yuiblog.com/blog/?p=1324</guid>
		<description><![CDATA[John Lindal (@jafl5272 on twitter) is one of the lead engineers working on the foundation on which Yahoo! APT is built. Previously, he worked on the Yahoo! Publisher Network. Checkboxes and radio buttons are well known patterns for choosing from a small set of items. The former lets you choose any subset of items (including [...]]]></description>
			<content:encoded><![CDATA[<div class="interview">
<div class="intro">
<p><em><a href="http://jjlindal.net/jafl/blog/">John Lindal</a> (<a href="http://twitter.com/jafl5272/">@jafl5272</a> on twitter) is one of the lead engineers working on the foundation on which <a href="http://apt.yahoo.com/">Yahoo! APT</a> is built.  Previously, he worked on the Yahoo! Publisher Network.</em></p>
</div>
</div>
<p>Checkboxes and radio buttons are well known patterns for choosing from a small set of items.  The former lets you choose any subset of items (including none), while the latter requires exactly one selection.</p>
<p>But what if you need a different behavior?  The <a href="http://yuilibrary.com/gallery/show/checkboxgroups">Checkbox Groups module</a> in the <a href="http://yuilibrary.com/gallery/">YUI 3 Gallery</a> implements three common cases and a foundation for constructing others.  The module is based on checkboxes because, by default, they do not enforce any restrictions, which makes them an ideal foundation on which to build.</p>
<p>The first behavior provided by the module is <code>Y.AtLeastOneCheckboxGroup</code>.  This enforces that at least one item must be selected.  More than one selection is permitted, but deselecting all items is prevented.  This implemented using the &#8220;drop of mercury&#8221; algorithm discussed in <a href="http://search.yahoo.com/search?p=tog+on+interface"><cite>Tog on Interface</cite></a>:  Whenever you try to deselect the last item, the selection slides out from under the cursor.  You can play with it <a href="http://jafl.github.com/yui3-gallery/checkboxgroups/">here</a>.</p>
<p>The second example (<code>Y.AtMostOneCheckboxGroup</code>) allows no selection, but more than one selection is not permitted.  Note that you cannot use radio buttons for this, because then it is not possible to unselect the current item.  This is demonstrated in the second example on <a href="http://jafl.github.com/yui3-gallery/checkboxgroups/">this page</a>.</p>
<p>The final example (<code>Y.SelectAllCheckboxGroup</code>) implements a &#8220;select all&#8221; behavior using an extra checkbox.  Selecting the &#8220;Select All&#8221; checkbox selects all the other items.  Deselecting it deselects all other items.  Selecting or deselecting any item updates the state of the &#8220;Select All&#8221; checkbox.  You can try it out by playing with the third example on <a href="http://jafl.github.com/yui3-gallery/checkboxgroups/">this page</a>.</p>
<p>The possiblities are endless.  You can build your own custom behavior quickly by extending the base class used by all the above examples:  <a href="http://jafl.github.com/yui3-gallery/checkboxgroups/yuidoc/CheckboxGroup.html"><code>Y.CheckboxGroup</code></a>.  This takes care of all the bureaucracy, so all you have to do is implement <code>enforceConstraints()</code>.  The function is invoked with the list of managed checkboxes and the index of the checkbox that has just been changed.  You can then inspect and update the state of all the checkboxes to enforce your custom constraints.</p>
<p>In many cases, all you need are the checkboxes themselves, e.g., <code>Y.AtLeastOneCheckboxGroup</code> and <code>Y.AtMostOneCheckboxGroup</code>.  For this, your constructor can be pass-through, since the base class <code>Y.CheckboxGroup</code> will manage the list for you.  If you need additional controls, e.g., <code>Y.SelectAllCheckboxGroup</code>, your constructor should require references to these controls, and you will need to store them so you can access their state inside <code>enforceConstraints()</code>.</p>
<p>To use the Checkbox Groups module, include the following script on your page:</p>
<pre>
&lt;script type="text/javascript" src="http://yui.yahooapis.com/combo?3.0.0/build/yui/yui-min.js&amp;gallery-2009.12.08-22/build/gallery-checkboxgroups/gallery-checkboxgroups-min.js"&gt;&lt;/script&gt;
</pre>
<p>The provided behaviors are all install-and-forget:</p>
<pre>
YUI().use('gallery-checkboxgroups', function(Y)
{
	// attaches behavior to all checkboxes with CSS class "my-at-least-one-checkbox-group"
	new Y.AtLeastOneCheckboxGroup('.my-at-least-one-checkbox-group');

	// attaches behavior to all checkboxes with CSS class "my-at-most-one-checkbox-group"
	new Y.AtMostOneCheckboxGroup('.my-at-most-one-checkbox-group');

	// attaches behavior to all checkboxes with CSS class "my-select-all-checkbox-group",
	// controlled by the checkbox with id "my-select-all-checkbox"
	new Y.SelectAllCheckboxGroup('#my-select-all-checkbox', '.my-select-all-checkbox-group');
});
</pre>
<p>One final note:  Ideally, checkboxes with custom behavior should be styled differently, so the user has some indication that they are not just plain checkboxes.  For example, Tog suggests using diamonds for <code>Y.AtLeastOneCheckboxGroup</code>.  In practice, however, you must also ensure that people can figure out that your controls are to be used for selecting items.  So be clever, just not too clever!</p>
]]></content:encoded>
			<wfw:commentRss>http://www.yuiblog.com/blog/2010/03/01/gallery-checkbox-group/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>

