Demonstrates a method of linking two DataTables together. In this case we link a row selection within a Master (or "parent") table + to creation of a separate Detail (or "child") table. This is a common usage case for datasets that may have related rows within + different datasets or as a result of typical database one-to-many key relationships. +
+Sample Data
+ +Let's assume we have an array of data that includes parent elements and children elements. The example we'll use defines several animal +categories and for each category it provides the names of some common characters from literature or pop culture of each type (except for the lowly +amoeba, we couldn't think of any ...).
+var animal_data = [
+ { aname: 'Lions', chars:[ 'Leo', 'Simba', 'Elsa', 'Cowardly Lion' ] },
+ { aname: 'Amoebas' },
+ { aname: 'Tigers', chars:[ 'Shere Kahn', 'Tigger', 'Tony' ] },
+ { aname: 'Mules', chars:[ 'Francis' ] },
+ { aname: 'Bears', chars:[ 'Smokey', 'Reginald', 'Winnie-the-Pooh', 'Baloo', 'Yogi' ] },
+ { aname: 'Snakes', chars:[ 'Kaa', 'The Serpent', 'Nagini' ] }
+];
+
+
+The DataTables
+ +Two DataTables are utilized for this example and for convenience they operate using the same animal_data JavaScript array. In most practical applications
+the data would probably be received from a remote source via DataSource or using the Model sync capability.
+
The "Master" table
+ +Our primary DataTable consists of two columns, aname which is the category of the animals and the other column is
+a calculated (or "unbound") column that is populated by a custom formatter. The custom formatter for nchars simply returns the length of the chars array
+associated with the record, or zero if none are defined.
var dt_master = new Y.DataTable({
+ columns : [
+ { key:'aname', label:'Type' },
+ { name:'nchars', label:'No. of Chars',
+ formatter: function(o){
+ return ( o.data.chars ) ? o.data.chars.length : 0;
+ }
+ }
+ ],
+ data : animal_data,
+ width: 200,
+ caption: 'Select an animal category below:'
+}).render("#mtable");
+
+
+Since we will need a click handler to track TR clicks on the Master DataTable, we will define a new
+attribute selectedRow and setup a TR click handler that assigns this attribute on a click.
//
+// Add a new attribute to track the last TR clicked,
+// this is used in the details DT formatter below and later
+// in the row click handler `delegate` for row highlighting
+//
+// also setup a click listener to update the "selectedRow" attribute on TR clicks
+//
+dt_master.addAttr("selectedRow", { value: null });
+
+dt_master.delegate('click', function (e) {
+ this.set('selectedRow', e.currentTarget);
+ }, '.yui3-datatable-data tr', dt_master);
+
+
+The "Detail" table
+ +We can proceed with defining the linked child table and rendering it initially because we have hidden
+this DataTable within a DIV with style display:none; (the DIV becomes visible on the first row click). This child DataTable consisits of
+another calculated (i.e. unbound) column aname (which just fills with the parent category name) and
+ a column char_name. The data for this table is initially empty, but will be populated by the click handler.
var dt_detail = new Y.DataTable({
+ columns : [
+ { name:'aname', label:'Animal Category',
+ formatter: function(o){
+ // just retrieve the selected Master record and return the "aname" column
+ var parent_rec = dt_master.getRecord( dt_master.get('selectedRow') );
+ return parent_rec.get('aname');
+ }
+ },
+ { key:'char_name', label:'Character' }
+ ],
+ data : [],
+ strings : {
+ emptyMessage : "No critter characters were found!"
+ },
+ width: 350,
+ caption: 'Characters of the category include:'
+}).render("#dtable");
+
+
+The selectedRow Listener
+
+ The "glue" between the master and detail DataTables is the delegated click handler on
+ the Master DataTable's rows -- or more specifically, the selectedRowChange event handler. When a row is clicked and the
+ selectedRow is changed, the underlying record from the Master table is
+ determined and the Detail DataTable is populated with the corresponding chars data from the clicked record.
+ We also handle TR highlighting for the clicked row by toggling a background color within this delegate handler.
+
dt_master.after('selectedRowChange', function (e) {
+
+ var tr = e.newVal, // the Node for the TR clicked ...
+ last_tr = e.prevVal, // " " " the last TR clicked ...
+ rec = this.getRecord(tr); // the current Record for the clicked TR
+
+ //
+ // This if-block does double duty,
+ // (a) it tracks the first click to toggle the "details" DIV to visible
+ // (b) it un-hightlights the last TR clicked
+ //
+ if ( !last_tr ) {
+ // first time thru ... display the Detail DT DIV that was hidden
+ Y.one("#chars").show();
+ } else {
+ last_tr.removeClass("myhilite");
+ }
+
+ //
+ // After unhighlighting, now highlight the current TR
+ //
+ tr.addClass("myhilite");
+
+
+ //
+ // Collect the "chars" member of the parent record into an array of
+ // objects with property name "aname"
+ //
+ var detail_data = [];
+ if ( rec.get('chars') ) {
+ Y.Array.each( rec.get('chars'), function(item){
+ detail_data.push( {char_name:item});
+ });
+ }
+
+ //
+ // Set the "detail_data" to the dt_detail DataTable
+ // also update the heading in "acategory"
+ // ( it automatically refreshes )
+ //
+ dt_detail.setAttrs({
+ data: detail_data,
+ caption: 'Characters of the <strong>' + rec.get('aname') +
+ '</strong> category include:'
+ });
+});
+
+
+
+ Note: In the case of the use of remote data via DataSource, the
+ selectedRowChange handler could be modified to generate a sendRequest
+ or similar remote call for the Detail data and the on:success handler
+ could be setup to set the data attribute.
+
Full Source Code
+ +CSS
+.yui3-skin-sam .yui3-datatable-caption {
+ font-size: 13px;
+ font-style: normal;
+ text-align: left;
+}
+
+.yui3-datatable-col-nchars {
+ text-align: center;
+}
+
+.yui3-skin-sam .yui3-datatable tr.myhilite td {
+ background-color: #C0ffc0;
+}
+
+#mtable tbody tr { /* Turn on cursor to show TR's are selectable on Master DataTable only */
+ cursor: pointer;
+}
+
+
+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 id="template" class="yui3-skin-sam dt-example yui3-g"> <!-- You need this skin class --> + <div class="yui3-u-1-3" id="mtable"></div> + + <!-- This is the HTML section for the "Details" markup ... + NOTE: it is hidden initially !! --> + <div class="yui3-u-2-3" id="chars" style="display:none;"> + <div id="dtable"></div> + </div> +</div>+ + +
Javascript
+YUI().use( "datatable", function (Y) {
+
+ var animal_data = [
+ { aname: 'Lions', chars:[ 'Leo', 'Simba', 'Elsa', 'Cowardly Lion' ] },
+ { aname: 'Amoebas' },
+ { aname: 'Tigers', chars:[ 'Shere Kahn', 'Tigger', 'Tony' ] },
+ { aname: 'Mules', chars:[ 'Francis' ] },
+ { aname: 'Bears', chars:[ 'Smokey', 'Reginald', 'Winnie-the-Pooh', 'Baloo', 'Yogi' ] },
+ { aname: 'Snakes', chars:[ 'Kaa', 'The Serpent', 'Nagini' ] }
+ ];
+
+ //
+ // Create the "parent" DataTable
+ //
+ var dt_master = new Y.DataTable({
+ columns : [
+ { key:'aname', label:'Type' },
+ { name:'nchars', label:'No. of Chars',
+ formatter: function(o){
+ return ( o.data.chars ) ? o.data.chars.length : 0;
+ }
+ }
+ ],
+ data : animal_data,
+ width: 200,
+ caption: 'Select an animal category below:'
+ }).render("#mtable");
+
+ //
+ // Add a new attribute to track the last TR clicked,
+ // this is used in the details DT formatter below and later
+ // in the row click handler `delegate` for row highlighting
+ //
+ // also setup a click listener to update the "selectedRow" attribute on TR
+ // clicks
+ //
+ dt_master.addAttr("selectedRow", { value: null });
+
+ dt_master.delegate('click', function (e) {
+ this.set('selectedRow', e.currentTarget);
+ }, '.yui3-datatable-data tr', dt_master);
+
+ //
+ // Create the characters DataTable and render it (it is hidden initially)
+ //
+ var dt_detail = new Y.DataTable({
+ columns : [
+ { name:'aname', label:'Animal Category',
+ formatter: function(o){
+ // just retrieve the selected Master record and return the
+ // "aname" column
+ var parent_rec = dt_master.getRecord(
+ dt_master.get('selectedRow') );
+
+ return parent_rec.get('aname');
+ }
+ },
+ { key:'char_name', label:'Character' }
+ ],
+ data : [],
+ strings : {
+ emptyMessage : "No critter characters were found!"
+ },
+ width: 350,
+ caption: 'Characters of the category include:'
+ }).render("#dtable");
+
+ //
+ // Setup a listener to the Master "selectedRowChange" event (i.e. after a
+ // row click)
+ //
+ dt_master.after('selectedRowChange', function (e) {
+
+ var tr = e.newVal, // the Node for the TR clicked ...
+ last_tr = e.prevVal, // " " " the last TR clicked ...
+ rec = this.getRecord(tr); // the current Record for the clicked TR
+
+ //
+ // This if-block does double duty,
+ // (a) it tracks the first click to toggle the "details" DIV to visible
+ // (b) it un-hightlights the last TR clicked
+ //
+ if ( !last_tr ) {
+ // first time thru ... display the Detail DT DIV that was hidden
+ Y.one("#chars").show();
+ } else {
+ last_tr.removeClass("myhilite");
+ }
+
+ //
+ // After unhighlighting, now highlight the current TR
+ //
+ tr.addClass("myhilite");
+
+
+ //
+ // Collect the "chars" member of the parent record into an array of
+ // objects with property name "aname"
+ //
+ var detail_data = [];
+ if ( rec.get('chars') ) {
+ Y.Array.each( rec.get('chars'), function(item){
+ detail_data.push( {char_name:item});
+ });
+ }
+
+ //
+ // Set the "detail_data" to the dt_detail DataTable
+ // also update the heading in "acategory"
+ // ( it automatically refreshes )
+ //
+ dt_detail.setAttrs({
+ data: detail_data,
+ caption: 'Characters of the <strong>' + rec.get('aname') +
+ '</strong> category include:'
+ });
+ });
+
+});
+
+