The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.

NAME

HTML::EditableTable - Classes for html presentation of tabular data with view and edit modes.

VERSION

Version 0.21

SYNOPSIS

 use HTML::EditableTable;
 use HTML::EditableTable::Horizontal; 

  my @tableData = 
    (
     {
       'part_id' => 7765,
       'catalog_id' => 'UX35AT',
       'addition_date' => '2008-10-10',
       'part_name' => 'control module',
       'vendor' => 'Praxis',
       'description' => 'ABS package with revA firmware.  Used in low-cost applications and as replacement for model UX34AT.  Includes adaptor wiring harness for UX34AT',
       'qa_results' => 'see http://yoururl.com/index.cgi?context=qa',
       'qoh' => '65',
       'rohs_category' => 2,
       'reorder_class' => 'C',
       'last_order_date' => '2010-06-10',
     },
     {
       'part_id' => 7961,
       'catalog_id' => 'ZX42AT',
       'addition_date' => '2009-03-01',
       'part_name' => 'power regulator',
       'vendor' => 'Armscor',
       'description' => 'Minature power supply with redundant relays',
       'qa_results' => '2ppm confirmed',
       'qoh' => '32',
       'rohs_category' => 2,
       'reorder_class' => 'A',
       'last_order_date' => '2009-12-17',
     },
    {
       'part_id' => 8055,
       'catalog_id' => 'UX24AT',
       'addition_date' => '2007-04-08',
       'part_name' => 'control module',
       'vendor' => 'Subarashii',
       'description' => 'Obsolete control module for A45 overthruster.  Requires UX27AZ conditioner and 3F buffering caps if the overthruster runs >18psi',
       'qa_results' => 'see http://yoururl.com/index.cgi?context=qa',
       'qoh' => '2',
       'rohs_category' => 4,
       'reorder_class' => 'A',
       'last_order_date' => '2005-08-19',
     },
    );

 my @tableFields =
    (
     {
       'editOnly' => 1,
       'formElement' => 'deleteRowButton',
     },
     {
       'dbfield' => 'part_id',
       'label' => 'Part Id',
       'viewOnly' => 1,
     },
     {
       'dbfield' => 'catalog_id',
       'label' => 'Catalog Id',
       'formElement' => 'textfield',
       'size' => 15,
       'uniquifierField' => 'part_id',
     },
     {
       'dbfield' => 'addition_date',
       'label' => 'Available From',
       'formElement' => 'calendar',
       'uniquifierField' => 'part_id',
     },
     {
       'dbfield' => 'part_name',
       'label' => 'Part Name',
       'formElement' => 'textfield',
       'size' => 20,
       'uniquifierField' => 'part_id',
     },
     {
       'dbfield' => 'vendor',
       'label' => 'Vendor',
       'formElement' => 'popup',
       'selectionList' => ['', 'Amexx', 'Armscor', 'Consolidated', 'Gentine', 'Oroco', 'Praxis',  'Shellalco', 'Subarashii',],
       'uniquifierField' => 'part_id',
     },
     {
       'dbfield' => 'description',
       'label' => 'Part Description',
       'formElement' => 'textarea',
       'subBr' => 1,
       'drillDownTruncate' => 60,
       'uniquifierField' => 'part_id',   
     },
     {
       'dbfield' => 'qa_results',
       'label' => 'QA Results',
       'formElement' => 'textfield',
       'linkifyContentOnView' => 1,
       'uniquifierField' => 'part_id',
     },
     {
       'dbfield' => 'qoh',
       'label' => 'Quantity',
       'formElement' => 'textfield',
       'size' => 5,
       'uniquifierField' => 'part_id',
     },
     {
       'dbfield' => 'rohs_category',
       'label' => 'RoHS',
       'formElement' => 'popup',
       'selectionList' => ['',1..10],
       'selectionLabels' => {
         1 => 'Large and small household appliances',
         2 => 'IT equipment',
         3 => 'Telecommunications equipment',
         4 => 'Consumer equipment',
         5 => 'Lighting equipment',
         6 => 'Electronic and electrical tools',
         7 => 'Toys, leisure, and sports equipment',
         8 => 'Medical devices',
         9 => 'Monitoring and control instruments',
         10 => 'Automatic dispensers',
       },
       'uniquifierField' => 'part_id',
     },
     {
       'dbfield' => 'reorder_class',
       'label' => 'Reorder Class',
       'formElement' => 'popup',
       'selectionList' => ['', 'A', 'B', 'C'],
       'uniquifierField' => 'part_id',
     },
     {
       'dbfield' => 'last_order_date',
       'label' => 'Last Ordered',
       'formElement' => 'calendar',
       'uniquifierField' => 'part_id',
     },     
    );

 ######## CGI Controller ##########

 my $t = CGI->new();
 print $t->header();

 my $context = $t->param('context') || 'view';

 my $table = HTML::EditableTable::Horizontal->new
    (
     {
       'tableFields' => \@tableFields,
       'width' => '100%',
       'jsAddData' => 1,
       'editMode' => $context,
       'data' => \@tableData,
       'jsSortHeader' => 1,
     }
    );

 print "<form method=post>";
 
 $table->htmlDisplay();
 
 my $nextContext = $context eq 'view' ? 'edit' : 'view';

 print "<input type=submit name=context value=$nextContext>";
 print "</form>";

DESCRIPTION

This module was developed to simplify the manipuation of complex tabular data in engineering and business-process web applications. The motivation was a rapid-prototype software development flow where the requirements gathering phase goes something like "I have this big complicated spreadsheet that I want to make a website out of..., can you help?". The EditableTable class is an 'abstract' base class and EditableTable::Horizontal and EditableTable::Vertical are the implementations for two commonly used table types. Key features of these classes are as follows:

- toggling of the table between view and edit modes with support for common html widgets

- uniquification of form element data to support processing of html form submissions of table data

- support for rowspanning

- methods to generate javascript for dynamic addition and remove of rows and commonly needed features such as 'click-to-expand' text display, calendar widget date entry, and sorting

- support for callbacks when data need to be pre-processed prior to display

For the Horizontal table, data are provided to tables in an array-of-hashes (most common case) or a hash-of-hashes. For the Vertical table, a single hash of data produces a single column of data while a hash-of-hashes supports muliple data columns.

TABLE METHODS AND PARAMETERS

The class methods are designed along 'public' and 'private' lines. The intended use model is indicated in the method descriptions.

new (public)

Common constructor for EditableTable-derived classes. Providing the required initialization data to new() can be done either by a hashref to table-level parameters or by calling the required set*() methods prior to rendering the table with htmlDisplay(). The following examples detail the minimum requirements for configuring an EditableTable.

 my $table = EditableTable::Horizontal->new
   (
     {
       'tableFields' => \@tableFields;
       'data' => \@data,
       'editMode' => 'edit'
     }
   );

or

 my $table = EditableTable::Horizontal->new()
 $table->setTableFields(\@tableFields);
 $table->setData(\@data);
 $table->setEditMode('edit'); 

initialize (private)

Peforms validation of data provided to the constuctor and makes set*() calls.

setValidateTableFieldKeys (public)

Toggles validation of field-level parameters. Enabled by default. Disable only if validation is a performance issue.

 $table->setValidateTableFieldKeys(0)

isvalidTableFieldKey (private)

Called for each field-level key parameer if validateTableFieldKeys is enabled (the default).

getConfigParams (private)

this is used by the Javascript Object to determine which javascript code to write for the table

isParamSet (private)

this is used by the Javascript Object to determine which javascript code to write for the table

setTableId (public)

Sets the html 'id' attribute for the top level <table> tab

 $table->setTableId('catalog_table_2')

getTableId (public)

Returns the 'tableId', which represents the <table> id attribute

 $table->getTableId()

setData (public)

Required parameter. The data structure provided to an EditableTable can take the following forms:

array of hashrefs (most common for Horizontal Table)

 $table->setData(
    [
      {
        'id' => 1001,
        'name' => 'wiring harness'
      },
      {
        'id' => 1002,
        'name' => 'wiring harness (new style)'
      }
    ]
 )

a hashref of hashrefs - this allows sorting the rows by hash key. This structure is allowable for EditableTable::Horizontal and multi-column Editable::Vertical. See "SORTING" for details.

 $table->setData(
    {
      '1001' => {
                  'id' => 1001,
                  'name' => 'wiring harness'
                },
      '1002' => {
                  'id' => 1002,
                  'name' => 'wiring harness (new style)'
                }
    }

a hashref - used for single column EditableTable::Vertical

 $table->setData( 
                  {
                    'id' => 1001,
                    'name' => 'wiring harness',
                  }
                )

getData (public)

returns the reference to the 'data' parameter

setTableFields (public)

Required parameter. An arrayref of hashrefs to parameters for each table field. Fields are presented left-to-right in array order for Horizontal tables and top-to-bottom for Vertical Tables. See "TABLE FIELD PARAMETERS" for documentation of the field parameters.

 $table->setTableFields (
   [
     {
       'dbfield' => id,
       'label' => 'ID#'
    },
    {
       'dbfield' => name,
       'label' => 'Name'
    }
   ];

getTableFields (public)

Returns an arrayref to the 'tableFields' table parameter.

setEditMode (public)

Required parameter. Set the table mode to 'view' or 'edit'. In 'view' mode, a table field is reprsented by text. In 'edit' mode, the a field is represented by 'its formElement' parameter.

 $table->setEditMode('edit');

setStringOutput (public)

By default, EditableTable's htmlDisplay and htmlJavascriptDisplay method will output to STDOUT. If stringOutput is set to 1 or 'true', then htmlDisplay will return a string with the table html.

 $table->setStringOutput(1);
 my $tableHtml = $table->htmlDisplay();

setSortHeader (public)

Set a base url for server-side sorting. See "SORTING" for details on the sorting options for EditableTable.

 $table->setSortHeader("http://yoururl.com?session=ruggs98888&");

setSortData (public)

When set, EditableTable will sort the $self->{data} server-side. Often this is unecessary as the data will have been presorted using SQL, but for small tables this is less work and is self-contained to the EditableTable class.

 $table->setSortData(1);

setJsSortHeader (public)

When set, implements javascript for client-side table sorting. See "SORTING" for details on EditableTable sorting.

 $table->setSortData(1)

setTabindex (public)

Use when the EditableTable needs to be integrated with other form elements and the tabindex isn't desirable. This sets all the tabindex values for formElemnts to the specified number, which will result in the browser default behavior being applied to the table but in the correct order with other form elements. An reference is used in case the behavior of this method is changed in the future.

 my $tabindex = 100;

 $table->setTabindex(\$tabindex);

setTitle (public)

Sets table title using colspanned <th> tag. Note that this method only works on Vertical tables as it conflicts with the javascript client-side sorting.

 $self->setTitle("Table Title")

setWidth (public)

sets the <table> 'width' parameter in pixels. Alternatively, use setStyle.

 $table->setWidth(1024);

setBorder (public)

sets the <table> 'border' param. Alternatively, use setStyle.

 $table->setBorder(1);

setStyle (public)

sets <table> 'style' attribute.

 $table->setStyle("border-width:1px;");

setJsAddData (public)

For Horizontal tables. Use to activate javascript to support addition of new table rows. See "JAVASCRIPT INTEGRATION" for details.

 $table->setJsAddData(1);

setNoHeader (public)

For Horizontal Tables. Suppresses the table header row.

 $self->setNoHeader(1);

setRowspannedEdit (public)

By default, a rowspanned table will flatten in 'edit' mode, with the rowspanning column repeated for each of the spanned rows. This is done to enable editing of the relationship betwen the fields and to preserve unique ids. If this is not the desired behavior, use this method to preserve the rowspanning in 'edit' mode.

 $table->setRowspannedEdit(1);

setSortOrder (public);

For Horizontal tables with a hash of hashes data structure, this sorts the rows per the provided arrayref. For Vertical tables with multiple data columns, sorts the columns left-to-right per the provided arrayref.

 $table->setSortOrder(['UX34IG' , 'UX45ZZ', 'RG01IG']);\

setSuppressUndefinedFields (public)

For Vertical tables. Avoids displaying a row if the data value for that row is undefined. Useful for using a single key set with partial data.

 $table->setSuppressUndefinedFields(1)

getCalendarDir (public)

Returns the directory for installation of www.dynarch.com jscalendar-1.0, which is supported in the 'calendar' formElement. Defaults to 'jscalendar' if not set.

 $self->getCalendarDir();

setCalendarDir (public)

Directory for installation of www.dynarch.com jscalendar-1.0, which is supported in the 'calendar' formElement. Defaults to 'jscalendar' if not set.

 $self->setCalendarDir('jscal_10');

htmlDisplay (public)

This method renders the html to STDOUT. If table requirements are not met, an exception will occur when this method is called.

 $table->htmlDisplay()

removeField (public)

Use to elminate a member of the 'tableFields'. Returns the table field hashref. Requires the key and the value to identify the field to remove

 $table->removeField('dbfield', 'partId');
 $table->removeField('label', 'Part Id#');  

setColumnLabel (public)

Given a 'dbfield' table field value, replaces the 'label' parameter with the supplied value.

 $table->setColumnLabel('partId', 'Part Id#');

shiftField (public)

shifts and returns a 'tableField'

 my $field = $table->shiftField();

unshiftField (public)

unshifts the 'tableFields' with the provided tableField hashref.

 my $field = { 'dbfield' => 'partId', 'label' => 'Part Id#' };
 $table->unshiftField($field);

setJavascript (public)

This method can be used to override the default javascript object. Inherit from HTML::EditableTable::Javascript and provide an object reference to your table prior to calling htmlJavascriptDisplay(). If this method is not used, the default class will be used to create a javascript object. See "JAVASCRIPT INTEGRATION" for more details.

 my $javascript = MyJavascript->new();
 $table->setJavascript($javascript);

htmlJavascriptDisplay (public, but normally called automatically)

Call it only if there is a need to control where javascript code is placed in the html. Prints the <script> javascript code needed for the table. The Javascript object determines the code required for the table. Normally, this method is called automatically by EditableTable. If a HTML::EditableTable::Javascript object does not exist at the time of execution, one will be created.

 $table->htmlJavascriptDisplay();

resetJavascriptDisplayed (public) {

Resets both the table javascriptDisplayed and Javascript javascriptDisplayCount flag to 0. Use when you have a persistent server and blow away an html document with the generated code.

 $table->resetJavascriptDisplayed();

checkBool (private)

Used to validate flag inputs. 1 and 'true' are accepted as positive inputs. 0 and 'false' for negative inputs.

htmlAddDataSetup (private)

Used outside the table to create a <div> for the insertion of new table rows with javascript

makeTable (abstract protected)

Method which must be provided by a class dervied from EditableTable.

getTableTagAttributes (private)

processes and returns a string of attributes for the top-level <table> tag

staticTableRow (private);

Generates an html table row. Called by makeTable().

TABLE FIELD PARAMETERS

dbfield (frequent)

Specifies the data hash key for provided data for the table element. Also Specifies the form element base name that will be used. Typically, this is also a database field name.

 'dbfield' => part_id

label (frequent)

Specifies the table column header for horizontal tables and the first colum for vertical tables.

 'label' => 'Part Id#'   

formElement (frequent)

formElement specifies the html input field that will be used when the table is in 'edit' mode. Valid values are

  • calendar - Implements popup calendar using www.dynarch.com jscalendar 1.0. Requires this javascript library to be accessible. See "JAVASCRIPT INTEGRATION" for details.

  • checkbox - Implements checkbox html element

  • deleteRowButton - Combine with jsAddRow Table-level feature. Provides button to delete a table row.

  • html5Calendar - Alternative to calendar. Implements HTML5 'date' input type. Tested with Opera, which is the only browser supporting this HTML5 input as of this writing.

  • hidden - Implements hidden element type.

  • popup - implements CGI "poup"

  • scrollingList - implments CGI "scrolling_list"

  • textarea - implements textarea HTML element

  • textfield - implements html input of type 'text'

 'formElement' => 'checkbox'

selectionList (frequent)

An array reference to a list of values to display in a popup or scrollingList

 'selectionList' => [ '34GXT', '35TTG', '56YUG' ]

selectionLabels (frequent)

A hash reference to labels and values to present in a popup or scrollingList. The keys are displayed while the values are provided in the form.

 'selectionLabels' => { '34GXT' => 'Big Widget', '35TTG' => 'Medium Widget', '56YUG' => 'Small Widget' }

editOnly (frequent)

Don't display this field when the table is in 'view' mode.

 'editOnly' => 1

viewOnly (frequent)

Don't display this field when the table is in 'edit' mode.

 'viewOnly' => 1

default (frequent)

Provides default value for form elements when no value is present in the data

 'default' => 'UX56SG'

uniquifierField (frequent)

Sets the field whose 'dbfield' value is used to provide a unique name and id to other form elements in the same array (row or column, depending on the implementation) of data. This is done by appending an '_' and then the value of the specified dbfield. Typically, the 'dbfield' is a database table's unique 'id' field.

 my @tableFields = (
 {
  'dbfield' => 'part_id'
 }
 {
  'dbfield' => 'description',  
  'uniquifierField' => 'part_id'
  'formElement' => 'textfield',  
 }
 );

with data

 my @data = (
 'part_id' => 1234
  )

will produce

 <input type=text id='description_1234' name='description_1234'> 

This field can also be an arrayref to a list of fields. The values of each will be joined with '_' characters to create the field name and id.

drillDownTruncate (occasional)

Implements a javascript which truncates the text and provides a toggle to switch between full and truncated text. The value sets the number of characters which are displayed in 'truncated' mode.

 'drillDownTruncate' => 100

callback (occasional)

Provides a mechanism to process data with a custom function or method prior to display. Typically used for custom formatting, setting table values based on other values, or 'child' database queries.

 'callback' => &callbackFunction

 sub callbackFunction {}

or

 'callback' => $self->callbackClosure()

 sub callbackClosure {

   my $fp = function {
     # need the object to do something at callback time
     $self->doSomething()
   };

   return $fp;
 }

The callback interface is as follows

 &$fp(
      $row, # hashref to the current row of data
      $fieldParams, # hashref to the current field parameters
      $editMode,  # table mode - 'view' or 'edit'
      $rowspanSubcounter # used only for tables using rowspanning, provides rowspan count for current rowspan instance 
     );

checkBehavior (occasional)

Determines how checkboxes will behave when toggling between view and edit modes. Valid values are 'checked', 'checkedOnVal', and checkedOnTrue'. The later value will result in the checkbox being checked if the data value matches the pattern /^1|true|yes$/i

 'checkBehavior' => 'checkedOnTrue'

suppressCallbackOnEdit (occasional)

Prevents the call to the callback when the table is in 'edit' mode

 'suppressCallbackOnEdit' => 1

subBr (occasional)

Sometimes used to sub <br> tags for \n when presenting text data. Common use case is when a user cut and pastes an email into a textarea. In 'view' mode, 'subBr' will keep the email's formatting

style (occasional)

Used to provide a css style to the <td> element used to present a data value. This parameter can be used in lieu of several others - align, bgcolor

 'style' => "font-family:'Times New Roman';font-size:20px;"

align (occasional)

Sets the horizontal 'align' <td> html parameter. Valid values are 'left', 'right', and 'center'.

 'align' => 'center'

width (occasional)

Sets the desired column width in pixels. Sets the <td> 'width' parameter.

 'width' => 70

size (occasional)

Sets a textfield input tags 'size' parameter for the number of displayed characters. Applicable only in 'edit' mode. See also maxLength.

 'size' => 60

maxLength (occasional)

For a textfield input tag, sets the maximum number of characters which can be input. Sets the 'maxLength' html tag parameter. This defaults to 255 if no value is provided, the reasoning being the most textfields are mapped to a VARCHAR database datatype with a 255 char limit.

 'maxLength' => 64

tooltip (occasional)

Implements javascript to provide mouseover tooltips. The value sets the text to be displayed. See the "JAVASCRIPT INTEGRATION" section for details.

 'tooltip' => 'The master PDM part number'

linkifyContentOnView (occasional)

For text data, this feature creates matching hyperlinks out of any text beginning with "http://". This is not done in 'edit' mode.

 'linkifyContentOnView' => 1

subCommaForBr (rare)

For'poorly' normalized data presentation where a <br> tag is preferable to a ',' in text display

 'subCommaForBr' => 1

jsClearColumnOnEdit (rare)

For horizontal tables only. Provides a 'clear column' button in the column header to clear text from all fields in the column when the table is in 'edit' mode.

   'jsClearColumnOnEdit' => 1,

minimalEditSize (rare)

In table 'edit' mode, the default textfield width is 2X the value with a minimum value of 60 char. If 'minimalEditSize' is set, this reduces to 1.2X the value with a minium value of 15. In large tables, minimalEditSize helps keep the presentation clean.

 'minimalEditSize' => 1

bgcolor (rare)

Sets the background color of cell be setting the 'bgcolor' <td> parameter

 'bgcolor' => '#ff0000'

rowspanArrayKey (rare)

Rowspanning is sometimes done by creators of spreadsheets that are to be converted to web applications. The data for these cases is a hierarchical array of hashrefs. This parameter provides the name of the second level hash key which contains the nested data to be rowspanned. The 'dbfield' parameter is used to specify the top level key to the second level of data to be rowspanned.

 @rowspannedData = (
     {
       'part_id' => 'UX34GT',
       'sub_data' => [
                       {
                        'sub_assembly_name' => '34R',
                       },
                       {
                        'sub_assembly_name' => '23D',
                       }
                     ]
     },
     {
       'part_id' => 'RT67IV',
       'sub_data' => [
                       {
                        'sub_assembly_name' => '14G',
                       },
                       {
                        'sub_assembly_name' => '13R',
                       }
                     ]
      }
 );

 @tableFields = (
   {
    'dbfield' => 'part_id',
    'label' => 'Part ID#'
   },
   {
    'dbfield' => 'sub_data',
    'rowspanArrayKey' => 'sub_assembly_name',
    'label' => 'Part Sub-Assembly',
   }
 );

 will produce at table like this

 ---------------------------------
 | Part ID#  | Part Sub-Assembly |
 |-------------------------------|
 |           |        34R        |
 | UX34GT    |-------------------|
 |           |        23D        |
 |-------------------------------|
 |           |        14G        |
 | RT67IV    |-------------------|
 |           |        13R        |
 ---------------------------------

In 'edit' mode, the table is flattened to support row addition and deletion.

rowspanArrayUniquifier (rare)

For rowspanning tables. In 'edit' mode, it is critical to produce a traceable field id for elements that are rowspanned. The most simple way to do this is by specifing this parameter to be a member of the nested rowspanned data. Working from the previous example:

 @tableFields = (
   {
    'dbfield' => 'part_id',
    'label' => 'Part ID#'
   },
   {
    'dbfield' => 'sub_data',
    'rowspanArrayKey' => 'sub_assembly_name',
    'label' => 'Part Sub-Assembly',
    'rowspanArrayUniquifier' => 'sub_assembly_name'
   }
 );

 produces a table with the following unique id's in 'edit' mode.  Note the flattening of the table in 'edit' mode.  If this is not desired set the table-level flag 'rowspannedEdit'.

 -------------------------------------------------------
 | Part ID# |             Part Sub-Assembly            |
 |----------|------------------------------------------|
 |  UX34GT  |        34R (sub_assembly_name_34R)       |
 |----------|------------------------------------------|
 |  UX34GT  |        23D (sub_assembly_name_23D)       |
 |----------|------------------------------------------|
 |  RT67IV  |        14G (sub_assembly_name_14G        |
 |----------|------------------------------------------|
 |  RT67IV  |        13R (sub_assembly_name_13R)       |
 -------------------------------------------------------

This parameter can be combined with uniquifierField to produce a expanded unique id. This is handy when the rowspanned data cannot produce a unique field id or a binding relationship needs to be maintained.

 @tableFields = (
   {
    'dbfield' => 'part_id',
    'label' => 'Part ID#'
    'uniquifierField' => 'part_id'
   },
   {
    'dbfield' => 'sub_data',
    'rowspanArrayKey' => 'sub_assembly_name',
    'label' => 'Part Sub-Assembly',
    'uniquifierField' => part_id,
    'rowspanArrayUniquifier' => 'sub_assembly_name'
   }
 );

 produces a table with the following unique id's in 'edit' mode.  Note the flattening of the table in 'edit' mode.  If this is not desired set the table-level param 'rowspannedEdit'.

 -----------------------------------------------------------------------------
 | Part ID#                |                Part Sub-Assembly                |
 |-------------------------|-------------------------------------------------|
 | UX34GT (part_id_UX34GT) |        34R (sub_assembly_name_UX34G_34R)        |
 |-------------------------|-------------------------------------------------|
 | UX34GT (part_id_UX34GT) |        23D (sub_assembly_name_UX34GT_23D)       |
 |-------------------------|-------------------------------------------------|
 | RT67IV (part_id_RT67IV) |        14G (sub_assembly_name_RT67IV_14G)       |
 |-------------------------|-------------------------------------------------|
 | RT67IV (part_id_RT67IV) |        13R (sub_assembly_name_RT67IV_13R)       |
 -----------------------------------------------------------------------------

This parameter can also be arrayref to a list of second-level data keys if additional keys are required to acheive a unique name. For example

rowspanArrayKeyForUniquification (rare)

When rowspanned tables are switched to 'edit' mode, the table is flattened to provide the ability to change the relationships which create the spanning. To provide traceability for the fields which span, combine this parameter with rowspanArrayUniquifier (described above) to specify the first and second level keys of the data hierarchy to used for field name and id construction.

given data

 @tableFields = (
   {
    'dbfield' => 'part_id',
    'label' => 'Part ID#'
    'uniquifierField' => 'part_id',
    'rowspanArrayKeyforUniquification' => 'sub_data',
    'rowspanArrayUniquifier' => 'sub_assembly_name'
   },
   {
    'dbfield' => 'sub_data',
    'rowspanArrayKey' => 'sub_assembly_name',
    'label' => 'Part Sub-Assembly',
    'uniquifierField' => part_id,
    'rowspanArrayUniquifier' => 'sub_assembly_name'
   }
 );

 produces a table with the following unique id's in 'edit' mode.  Note the flattening of the table in 'edit' mode.  If this is not desired set the table-level param 'rowspannedEdit'.

 ---------------------------------------------------------------------------------
 | Part ID#                    |                Part Sub-Assembly                |
 |-----------------------------|-------------------------------------------------|
 | UX34GT (part_id_UX34GT_34R) |        34R (sub_assembly_name_UX34G_34R)        |
 |-----------------------------|-------------------------------------------------|
 | UX34GT (part_id_UX34GT_23D) |        23D (sub_assembly_name_UX34GT_23D)       |
 |-----------------------------|-------------------------------------------------|
 | RT67IV (part_id_RT67IV_14G) |        14G (sub_assembly_name_RT67IV_14G)       |
 |-----------------------------|-------------------------------------------------|
 | RT67IV (part_id_RT67IV_13R) |        13R (sub_assembly_name_RT67IV_13R)       |
 ---------------------------------------------------------------------------------

masterCounterUniquify (rare)

The final field name and id generation parameter. If specified, uses the 'class static' field counter to append a final id to the field name. Useful when adding rows of new data to a table where this is needed to ensure a unique id is created.

given data

 @tableFields = (
   {
    'dbfield' => 'part_id',
    'label' => 'Part ID#'
   },
   {
    'dbfield' => 'sub_data',
    'rowspanArrayKey' => 'sub_assembly_name',
    'label' => 'Part Sub-Assembly',
    'masterCounterUniquify' => 1
   }
 );

 produces a table with the following unique id's in 'edit' mode.  Note the flattening of the table in 'edit' mode.  If this is not desired set the table-level param 'rowspannedEdit'.

 ------------------------------------------------------
 | Part ID# |                Part Sub-Assembly        |
 |----------|-----------------------------------------|
 | UX34GT   |        34R (sub_assembly_name_1)        |
 |----------|-----------------------------------------|
 | UX34GT   |        23D (sub_assembly_name_2)        |
 |----------|-----------------------------------------|
 | RT67IV   |        14G (sub_assembly_name_3)        |
 |----------|-----------------------------------------|
 | RT67IV   |        13R (sub_assembly_name_4)        |
 ------------------------------------------------------

styleHandler (rare)

Callback mechanism to provide a style for the <td> tag.

 'styleHandler' => &getStyle

The interface for the callback is

 &getStyle(
   $row # hashref to the current data set 
 ) 

modeModifier (rare)

Provides a callback mechanism to determine if the mode ('view' or 'edit') should be changed for this field only

 'modeModifer' => &getMode

The interface for this callback is as follows:

 &getMode (
    $editMode, # 'view' or 'edit'
    $data # hashref to the current dataset (row or column, depending on table type)
 )

The callback must return 'view' or 'edit'.

editOnlyOnNegativeValue (rare)

Use this feature when it is desired to prevent editing on existing data and only allow edting of new data where the value is <0

 'editOnlyOnNegativeValue' => 1,

selectionListCallback (rare)

Specifies a callback to produce a value list for a popup or scrollingList element. Useful when a dynamic query or calculation must be made.

 'selectionListCallback' => &getListValues

The interface for this call back is as follows:

 &getListValues (
   $dataSet # current row or column data hashref
 );

The callback must provide an arrayref of values.

htmlSub (rare)

provides a simple mechanism to substitute the current field's value into a string template

 'htmlSub' => "http://www.site.com?value=<dbfield>"

The '<dbfield>' text will be replaced by the current dataset's $data->{dbfield}

JAVASCRIPT INTEGRATION

Overview

The javascript features are encapsulated in HTML::EditableTable::Javascript. Each method provided by this class provides a distinct javascript feature, allowing for clean override or extension. The intent is to provide all javascript functionality "under the hood" via table and table-field parameter setting. If not specified by the user, the Javascript object is created by the table. This leads to the the javascript <script> tags being inserted with the table. Only the code needed for the specified table functionality is generated.

To use a custom javascript class, call setJavascript() prior to htmlDisplay() or htmlJavascriptDisplay(). Note that the Javascript object requires a reference to the parent table so it must be created after the table.

Inherit from the HTML::EditableTable::Javascript class to extend or override javascript code generation.

 package MyNewJavascript {                                                                                                                                          
 @ISA = qw(HTML::EditableTable::Javascript);                                                                                                                        
   # new code
 }
 1;                                                                                                                                                                 

 my $table = HTML::EditableTable::Vertical->new(); 

 my $customJavascript = MyNewJavascript->new($table);                                                                                                               
 $table->setJavascript($customJavascript);                                                                                                                          

The functionality provided through javascript are described below:

Table Sorting

Client-side table sorting is implemented using Stuart Langridge's SortTable Version 2: http://www.kryogenix.org/code/browser/sorttable/ See "SORTING" for more details on this and other table sorting options. To use this feature set the 'jsSortHeader' table level parameter:

 $table->setJsSortHeader(1);

Click-to-Expand Text

This feature truncates text to the specified number of characters by default and provides a "more" button to display the full text. A click on the full text returns the cell to its truncated state. This feature is useful when the full text consumes a lot of screen real estate. To use this feature, set the 'drillDownTruncate' table field parameter to the number of characters to display in truncated mode:

 my $tableField = 
      {
       'dbfield' => 'description',
       'label' => 'Part Description',
       'drillDownTruncate' => 60,
     };

Mousover Tooltips

The mouseover tooltip feature is provided using javascript from David Flanagan's Javascript, The Definitive Guide. To use this feature set the table field 'tooltip' parameter:

 my $tableField = 
      {
       'dbfield' => 'description',
       'label' => 'Part Description',
       'tooltip' => 'Description to be displayed in catalog.'
     };

Add and Delete Table Rows

Client-side row addition and deletion is provided by javascript. When this feature is used, a <div> tag with a row template is written when htmlDisplay() is called. For row additions, sequential negative integers are used to provide unique identifiers to new form elements. For the following table field specification:

  @tableFields = (
   {
    'dbfield' => 'part_id',
    'label' => 'Part ID#'
    'formElement' => 'textField'
   },
   {
    'dbfield' => 'sub_data',
    'label' => 'Part Sub-Assembly',
    'formElement' => 'textField'
   }
 );

The first dynamically added row will have input field ids as follows:

 <input id="part_id_-1" type="textfield"/>
 <input id="sub_data_-1" type="textfield"/>

To use this feature, set the table-level parameter 'jsAddData':

 $table->jsAddData(1);

To provide a means to delete an added row, specify a table field with the following parameters:

 my $tableField = {
    'editOnly' => 1,
    'formElement' => 'deleteRowButton'
 }

Calendar Input Widget

Date entry is very common in the target applications of EditableTable. A formElement of type 'calendar' is provided by impementing dynarch.com's jscalendar-1.0 widget. The jscalendar library must be available to the webserver. The default directory is "./jscalendar", though this can be changed by setting the table-level parameter setCalendarDir. The calendar UI is implemented as clickable text which pops up the calendar widget. If no data value is present, "Click here to add date" is displayed. The Calendar setup is called with the following settings.

 Calendar.setup(
   inputField     :    $name,
   ifFormat       :    "%Y-%m-%d",
   displayArea    :    "showD_$name",
   daFormat       :    "%Y-%m-%d",
   cache          :    true
 )

To provide a jscalendar input, use the formElement type 'calendar',

 my $tablField = {
  'dbfield' => 'addition_date',
  'label' => 'Available From',
  'formElement' => 'calendar',
 },

Note also that html5 provides a (much appreciated) date entry input type. This is supported by using the formElement type 'html5calendar'. As of this writing only Opera supported this html5 feature, however, so the jscalendar integration will be useful for some time.

SORTING

There are four techniques available to sort HTML::EditableTable::Horizontal tables and one for multi-column HTML::EditableTable::Vertical. Each is described below along with examples.

Server-side by user

Typically used when the data for the table are presorted by SQL or in pre-processing. In this case, set the sortHeader url, which will call the cgi script with the appropriate column field value. The user is responsible for interpreting the cgi parameters and providing sorted data. Server side sorting is suitable for situations where the size of the html document and it's DOM challenge the browser's available RAM.

EditableTable appends 'orderByAsc=<dbfield>' or 'orderByDesc=<dbfield>' to the url set in the call to sortHeader(), so the url must be constructed by the user in anticipation of this. When the table is first displayed, the parameter 'orderByAsc' is appended. Subsequent clicks on the header toggle the appended paramter between 'orderbyDesc' and 'orderByAsc', providing for a reversible sort.

 $table->sortHeader("http://myscript.cgi?context=edit&");

An examle url attached to each column header by EditableTable is "http://myscript.cgi?context=edit&sortby=partId".

a simplified example of the server cgi code implementing the sort:

 my $t = CGI->new();
 my $sortby = $t->param('orderByAsc');
 my $sql = "select * from table order by $sortby";

Server-side by EditableTable

Reversible data sorting is handled by the EditableTable object. The user only needs to provide a value for sortHeader and set sortData to 1 - no additional server-side work is needed. This technique will sort rowspanned tables.

 $table->sortHeader("http://myscript.cgi?context=edit&");
 $table->sortData(1);

Server-side by setting sortKeys

This technique is appropriate for hash-of-hashes data structures in both Horizontal and multi-column Vertical tables. In this case, the user provides an arrayref to the sorted keys. Horizontal tables are sorted top-to-bottom in array order and multi-column Vertical tables left-to-right. For other types of tables the sortOrder is ignored.

 $table->setSortOrder( ['UX45TG', 'HU78OO', 'UV01TT'] );

Client-side by javascript

This technique implements client-side javascript to provide reversible sorting without calling the cgi. It does not currently work with rowspanning tables. The user need only set jsSortHeader to enable this functionality.

 $table->setJsSortHeader(1);

OTHER EXAMPLES

There are several examples provided in the 'example' directory in the module distribution. These examples also comprise the test case suite for this module.

AUTHOR

This code is provided courtesy of Freescale Semicondutor. The developers are

Andy Espenscheid <espenshovel@gmail.com>

Sergei Kondratiev

Vishesh Kumar

Vijay Yadav

Mike Boatright

BUGS & POTENTIAL ISSUES

  • The table method setTitle does not work with Horizontal tables. It conflicts with the javascript client-side sorting implmentation so it was disabled.

  • The javascript-based sorting with rowspanned tables does not work well

  • No development or testing was done with mod-perl. There are a few class static variables which may cause trouble.

  • There are places where Carp's confess() is used where carp() may be more appropriate. We tended to err on the side of 'throw exception first deal with the support calls later'

  • Combining 'rowspannedEdit' with a 'deleteRowButton' column doesn't work. This may be fixed in a future patch.

Please report any bugs or feature requests to bug-html-editabletable at rt.cpan.org, or through the web interface at http://rt.cpan.org/NoAuth/ReportBug.html?Queue=HTML-EditableTable. I will be notified, and then you'll automatically be notified of progress on your bug as I make changes.

SUPPORT

You can find documentation for this module with the perldoc command.

    perldoc HTML::EditableTable

You can also look for information at:

ACKNOWLEDGEMENTS

The authors would like to acknowledge Dave Benoit, David Corley, Lydia Hultquist, and Patti Rankin at Freescale Semicondutor.

COPYRIGHT & LICENSE

Copyright 2010 Freescale Semiconductor, all rights reserved.

This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself.