+ This example shows one method for managing a selection checkbox column + in a DataTable. +
+Processed:
+-
+
- (None) +
Sample Data
+ ++ This example will use a local Javascript array of data that includes some + common Internet port socket numbers and protocol names: +
+ +var ports = [
+ { port:20, pname:'FTP_data',ptitle:'File Transfer Process Data' },
+ { port:21, pname:'FTP', ptitle:'File Transfer Process' },
+ { port:22, pname:'SSH', ptitle:'Secure Shell' },
+ { port:23, pname:'TELNET', ptitle:'Telnet remote communications' },
+ ... // data continues
+];
+
+
+The DataTable
+ ++ Our DataTable for this example will utilize a custom formatter as the first + column, to display a standard HTML INPUT[type=checkbox] element as an + indication that the record is desired to be "selected" for additional + processing. But that checkbox won't work on its own, because if a "sort" + action happens after the checkbox is clicked the "check" status is lost! +
+ +
+ A way to get around this is to create a binding of the checkbox to an
+ attribute of each record which will remain with the record even upon
+ sorts, edits, or other modifications to the record. This is accomplished
+ by defining a custom recordType for the DataTable that incorporates all
+ of our standard data for our table but also defines a new Attribute
+ (called select here) that is a boolean value to track whether the record
+ is selected.
+
+ The implementation of these methods is shown below, where we have defined a
+ custom formatter and emptyCellValue for the "select" column that
+ creates a checked or unchecked checkbox depending on the state of the
+ attribute, and defines a custom recordType with our new attribute added.
+ Additionally, we incorporate (a) scrolling and (b) sorting to demonstrate
+ that this technique works.
+
var table = new Y.DataTable({
+ columns : [
+ { key: 'select',
+ allowHTML: true, // to avoid HTML escaping
+ label: '<input type="checkbox" class="protocol-select-all" title="Toggle ALL records"/>',
+ formatter: '<input type="checkbox" checked/>',
+ emptyCellValue: '<input type="checkbox"/>'
+ },
+ { key: 'port', label: 'Port No.' },
+ { key: 'pname', label: 'Protocol' },
+ { key: 'ptitle', label: 'Common Name' }
+ ],
+ data : ports,
+ scrollable: 'y',
+ height : '250px',
+ sortable : ['port','pname'],
+ sortBy : 'port',
+ recordType: ['select', 'port', 'pname', 'ptitle']
+}).render("#dtable");
+
+
+The Checkbox Listeners
+ ++ Having a DataTable with a bunch of checkboxes in it may look cool (or + not!), but we also need to define what to do when they are checked. Since + the column formatter for the first column creates the checkboxes, we + delegate "click" listeners from the DataTable for the two types of + checkboxes—the "check all" checkbox in the header and the individual + checkboxes on each data row. +
+ ++ Be sure to avoid subscribing to events + directly on elements in each row of a DataTable. +
+ +// Define a listener on the DT first column for each record's checkbox,
+// to set the value of `select` to the checkbox setting
+table.delegate("click", function(e){
+ // undefined to trigger the emptyCellValue
+ var checked = e.target.get('checked') || undefined;
+
+ this.getRecord(e.target).set('select', checked);
+
+ // Uncheck the header checkbox
+ this.get('contentBox')
+ .one('.protocol-select-all').set('checked', false);
+}, ".yui3-datatable-data .yui3-datatable-col-select input", table);
+
+
+// Also define a listener on the single TH checkbox to
+// toggle all of the checkboxes
+table.delegate('click', function (e) {
+ // undefined to trigger the emptyCellValue
+ var checked = e.target.get('checked') || undefined;
+
+ // Set the selected attribute in all records in the ModelList silently
+ // to avoid each update triggering a table update
+ this.data.invoke('set', 'select', checked, { silent: true });
+
+ // Update the table now that all records have been updated
+ this.syncUI();
+}, '.protocol-select-all', table);
+
+
+Button Click Handlers
+ ++ The bulk of the nitty-gritty is done now. We'll just wire up a button to + process the checked records and another to clear the selection. +
+ +function process() {
+ var ml = table.data,
+ msg = '',
+ template = '<li>Record index = {index} Data = {port} : {pname}</li>';
+
+ ml.each(function (item, i) {
+ var data = item.getAttrs(['select', 'port', 'pname']);
+
+ if (data.select) {
+ data.index = i;
+ msg += Y.Lang.sub(template, data);
+ }
+ });
+
+ Y.one("#processed").setHTML(msg || '<li>(None)</li>');
+}
+
+Y.one("#btnSelected").on("click", process);
+
+Y.one("#btnClearSelected").on("click",function () {
+ table.data.invoke('set', 'select', undefined);
+
+ // Uncheck the header checkbox
+ table.get('contentBox')
+ .one('.protocol-select-all').set('checked', false);
+
+ process();
+});
+
+
+
+ Note that another option for capturing all the checked checkboxes would be
+ table.get('contentBox').all('.yui3-datatable-col-select input:checked').
+ To make sure that was supported across all browsers, we'd then need to
+ include the selector-css3 module in our use() statement.
+
+ Another improvement that could be made for HTML5 compliant clients would be
+ to add in localStorage access to save the checked record data to the
+ browser environment. You can see how to do this in the
+ App Framework's Todo List example.
+
Full Code Listing
+ +CSS
+ +.yui3-skin-sam .yui3-datatable-col-select {
+ text-align: center;
+}
+
+#processed {
+ margin-top: 2em;
+ border: 2px inset;
+ border-radius: 5px;
+ padding: 1em;
+ list-style: none;
+}
+
+
+HTML Markup
+
+Note: be sure to add the yui3-skin-sam classname to the
+page's <body> element or to a parent element of the widget in order to apply
+the default CSS skin. See Understanding Skinning.
+
<div class="example yui3-skin-sam"> <!-- You need this skin class --> + <div id="dtable"></div> + <button id="btnSelected" class="yui3-button">Process Selections</button> + <button id="btnClearSelected" class="yui3-button">Clear Selections</button> + + <h4>Processed:</h4> + <ul id="processed"> + <li>(None)</li> + </ul> +</div>+ + +
Javascript
+ +YUI({ filter: 'raw' }).use( "datatable-sort", "datatable-scroll", "cssbutton", function (Y) {
+
+ var ports = [
+ { port:20, pname:'FTP_data',ptitle:'File Transfer Process Data' },
+ { port:21, pname:'FTP', ptitle:'File Transfer Process' },
+ { port:22, pname:'SSH', ptitle:'Secure Shell' },
+ { port:23, pname:'TELNET', ptitle:'Telnet remote communications' },
+ { port:25, pname:'SMTP', ptitle:'Simple Mail Transfer Protocol' },
+ { port:43, pname:'WHOIS', ptitle:'whois Identification' },
+ { port:53, pname:'DNS', ptitle:'Domain Name Service' },
+ { port:68, pname:'DHCP', ptitle:'Dynamic Host Control Protocol' },
+ { port:79, pname:'FINGER', ptitle:'Finger Identification' },
+ { port:80, pname:'HTTP', ptitle:'HyperText Transfer Protocol' },
+ { port:110, pname:'POP3', ptitle:'Post Office Protocol v3' },
+ { port:115, pname:'SFTP', ptitle:'Secure File Transfer Protocol' },
+ { port:119, pname:'NNTP', ptitle:'Network New Transfer Protocol' },
+ { port:123, pname:'NTP', ptitle:'Network Time Protocol' },
+ { port:139, pname:'NetBIOS',ptitle:'NetBIOS Communication' },
+ { port:143, pname:'IMAP', ptitle:'Internet Message Access Protocol' },
+ { port:161, pname:'SNMP', ptitle:'Simple Network Management Protocol' },
+ { port:194, pname:'IRC', ptitle:'Internet Relay Chat' },
+ { port:220, pname:'IMAP3', ptitle:'Internet Message Access Protocol v3' },
+ { port:389, pname:'LDAP', ptitle:'Lightweight Directory Access Protocol' },
+ { port:443, pname:'SSL', ptitle:'Secure Socket Layer' },
+ { port:445, pname:'SMB', ptitle:'NetBIOS over TCP' },
+ { port:993, pname:'SIMAP', ptitle:'Secure IMAP Mail' },
+ { port:995, pname:'SPOP', ptitle:'Secure POP Mail' }
+ ];
+
+ var table = new Y.DataTable({
+ columns : [
+ { key: 'select',
+ allowHTML: true, // to avoid HTML escaping
+ label: '<input type="checkbox" class="protocol-select-all" title="Toggle ALL records"/>',
+ formatter: '<input type="checkbox" checked/>',
+ emptyCellValue: '<input type="checkbox"/>'
+ },
+ { key: 'port', label: 'Port No.' },
+ { key: 'pname', label: 'Protocol' },
+ { key: 'ptitle', label: 'Common Name' }
+ ],
+ data : ports,
+ scrollable: 'y',
+ height : '250px',
+ sortable : ['port','pname'],
+ sortBy : 'port',
+ recordType: ['select', 'port', 'pname', 'ptitle']
+ }).render("#dtable");
+
+ // To avoid checkbox click causing harmless error in sorting
+ // Workaround for bug #2532244
+ table.detach('*:change');
+
+ //----------------
+ // "checkbox" Click listeners ...
+ //----------------
+
+ // Define a listener on the DT first column for each record's "checkbox",
+ // to set the value of `select` to the checkbox setting
+ table.delegate("click", function(e){
+ // undefined to trigger the emptyCellValue
+ var checked = e.target.get('checked') || undefined;
+
+ this.getRecord(e.target).set('select', checked);
+
+ // Uncheck the header checkbox
+ this.get('contentBox')
+ .one('.protocol-select-all').set('checked', false);
+ }, ".yui3-datatable-data .yui3-datatable-col-select input", table);
+
+
+ // Also define a listener on the single TH "checkbox" to
+ // toggle all of the checkboxes
+ table.delegate('click', function (e) {
+ // undefined to trigger the emptyCellValue
+ var checked = e.target.get('checked') || undefined;
+
+ // Set the selected attribute in all records in the ModelList silently
+ // to avoid each update triggering a table update
+ this.data.invoke('set', 'select', checked, { silent: true });
+
+ // Update the table now that all records have been updated
+ this.syncUI();
+ }, '.protocol-select-all', table);
+
+ //----------------
+ // CSS-Button click handlers ....
+ //----------------
+ function process() {
+ var ml = table.data,
+ msg = '',
+ template = '<li>Record index = {index} Data = {port} : {pname}</li>';
+
+ ml.each(function (item, i) {
+ var data = item.getAttrs(['select', 'port', 'pname']);
+
+ if (data.select) {
+ data.index = i;
+ msg += Y.Lang.sub(template, data);
+ }
+ });
+
+ Y.one("#processed").setHTML(msg || '<li>(None)</li>');
+ }
+
+ Y.one("#btnSelected").on("click", process);
+
+ Y.one("#btnClearSelected").on("click",function () {
+ table.data.invoke('set', 'select', undefined);
+
+ // Uncheck the header checkbox
+ table.get('contentBox')
+ .one('.protocol-select-all').set('checked', false);
+
+ process();
+ });
+
+});
+
+