This demo shows how to implement an advanced editable table by using the AJAX
capabilities of the jQuery library. The filters are refreshed everytime the table
content is loaded.
Imported javascript files:
<script type= "text/javascript" language= "javascript" src= "TableFilter/tablefilter_all.js" ></script> <script type="text/javascript" language= "javascript" src= "includes/jquery-1.7.2.min.js" ></script>
Configuration object and AJAX logic:
var config = { grid_layout: true , grid_width: '840px' , sort: true , alternate_rows: true , rows_counter: true , mark_active_columns: true , loader: true , loader_html: '<img src="TableFilter/TF_Themes/SkyBlue/images/img_loading.gif" alt="">' , loader_css_class: 'myLoader' , btn_reset: true , status_bar: true , col_1: 'select' , col_2: 'select' , col_5: 'select' , remember_grid_values: true , remember_page_number: true , col_width: ['50px' , '270px' , '210px' , '80px' , '80px' , '60px' ], paging: true , paging_length: 15, on_filters_loaded: function (o){ if (o.paging){ tf_Id( 'paging' ).checked = true ; } }, custom_cell_data_cols: [5], custom_cell_data: function (o, c, i){ if (i === 5){ var chk = c.getElementsByTagName( 'input' )[0]; if (chk.checked) return 'yes' ; else return 'no' ; } }, themes: { name:['SkyBlueTheme' ], src:['TableFilter/TF_Themes/SkyBlue/TF_SkyBlue.css' ], description:['SkyBlue stylesheet' ] }, extensions: { name:['ColumnsResizer' ], src:['TableFilter/TFExt_ColsResizer/TFExt_ColsResizer.js' ], description:['Columns Resizing' ], initialize:[function (o){o.SetColsResizer();}] }, col_resizer_all_cells: true , selectable: true , editable: true , ezEditTable_config: { loadStylesheet: true , default_selection: 'both' , editor_model: 'cell' , cell_editors: [ { type: 'none' }, { type: 'input' , attributes: [[ 'title' , 'First name and last name' ]] }, { type: 'input' , attributes: [[ 'title' , 'email address' ]] }, { type: 'none' }, { type: 'input' , attributes: [[ 'maxLength' , 10], [ 'title' , '10 digits max.\n numbers with 2 decimal places only' ]], css: 'alignRight' }, { type: 'boolean' } ], actions: { 'update' : { uri: 'php/employeeUpdate.php' , submit_method: 'form' , form_method: 'POST' , param_names: ['id' , 'name' , 'email' , 'startdate' , 'salary' , 'active' ], on_after_submit: function (o) { ajax.isRefreshed = false ; ajax.activeRowIndex = o.Selection.GetActiveRow().rowIndex; ajax.loadContent(); } }, 'insert' : { uri: 'php/employeeInsert.php' , submit_method: 'form' , form_method: 'POST' , param_names: ['id' , 'name' , 'email' , 'startdate' , 'salary' , 'active' ], default_record: ['' , 'Employee name...' , 'employee@email.com' , '2011-01-01' , '0.00' , '<input type="checkbox" checked="">' ], on_after_submit: function (o) { ajax.isRefreshed = false ; ajax.activeRowIndex = o.Selection.GetActiveRow().rowIndex; ajax.loadContent(); } }, 'delete' : { uri: 'php/script.deleteEmployee.php' , submit_method: 'script' , bulk_delete: false , param_names: ['id' ], on_after_submit: function (o) { ajax.isRefreshed = false ; ajax.loadContent(); } } }, on_added_dom_row: function (o) { tf.nbRows = tf.GetRowsNb(); }, validate_modified_value: function (o, colIndex, oldVal, newVal, cell, editor) { if (colIndex == 2 && oldVal != newVal) { if (!et_ValidateEmail(newVal)) { alert('Please insert a valid email!' ); return false ; } else return true ; } else if (colIndex == 4 && oldVal != newVal) { if (!et_IsNumber(newVal)) { alert('Please insert a valid number with 2 decimal places!' ); return false ; } else return true ; } else return true ; } } var ajax = { isRefreshed: false , loadContent: function () { if ( this .isRefreshed) { return ; } $('#loadLabel' ).html( 'Loading data...' ); this .load( 'php/getEmployees.php' ); }, load: function (uri){ var xhr = $.ajax({ url: uri + '?r=' + new Date().getTime(), context: $('#demo tbody' ), type: 'GET' , dataType: 'html' }) .done(function (data) { this .html(data); $('#loadLabel' ).html( '' ); if (tf) { if (tf.paging){ tf.paging = false ; } tf.nbRows = tf.GetRowsNb(); tf.RefreshFiltersGrid(); if (!tf.paging && tf_Id( 'paging' ).checked){ tf.RemovePaging(); tf.paging = true ; tf.SetPaging(); } tf.SetAlternateRows(); tf.ezEditTable.Editable.modifiedRows = []; tf.ezEditTable.Editable.addedRows = []; tf.ezEditTable.Editable.deletedRows = []; if (ajax.activeRowIndex){ tf.ezEditTable.Selection.SelectRowByIndex(ajax.activeRowIndex); } if (tf.paging && tf.currentPageNb > 0){ tf.SetPage(tf.currentPageNb); } ajax.isRefreshed = true ; } else { tf = new TF( 'demo' , config); tf.Init(); } }) .fail(function (jqXHR, textStatus){ $('#loadLabel' ).html(textStatus); this .html( 'Ouups an error occured! Data could not be loaded!' ); }); } }; function et_ValidateEmail(email) { var reg = /^([A-Za-z0-9_\-\.])+\@([A-Za-z0-9_\-\.])+\.([A-Za-z]{2,4})$/; return reg.test(email); } function et_IsNumber(val) { var isNumber = /^\d+\.\d{2}$/; return isNumber.test(val); } var tf; $(function (){ ajax.loadContent(); }); // filters configuration
var config = {
grid_layout: true,
grid_width: '840px',
sort: true,
alternate_rows: true,
rows_counter: true,
mark_active_columns: true,
loader: true,
loader_html: ' ',
loader_css_class: 'myLoader',
btn_reset: true,
status_bar: true,
col_1: 'select',
col_2: 'select',
col_5: 'select',
remember_grid_values: true,
remember_page_number: true,
col_width: ['50px', '270px', '210px', '80px', '80px', '60px'],
paging: true,
paging_length: 15,
on_filters_loaded: function(o){
if(o.paging){ tf_Id('paging').checked = true; }
},
/* Custom data delegate for Active column checkbox */
custom_cell_data_cols: [5],
custom_cell_data: function(o, c, i){
if(i === 5){
var chk = c.getElementsByTagName('input')[0];
if(chk.checked) return 'yes';
else return 'no';
}
},
/* Sky Blue theme */
themes: {
name:['SkyBlueTheme'],
src:['TableFilter/TF_Themes/SkyBlue/TF_SkyBlue.css'],
description:['SkyBlue stylesheet']
},
//Column resize feature
extensions: {
name:['ColumnsResizer'],
src:['TableFilter/TFExt_ColsResizer/TFExt_ColsResizer.js'],
description:['Columns Resizing'],
initialize:[function(o){o.SetColsResizer();}]
},
col_resizer_all_cells: true,
// selection and edition require ezEditTable extension
selectable: true,
editable: true,
// ezEditTable extension configuration
ezEditTable_config: {
loadStylesheet: true,
default_selection: 'both',
editor_model: 'cell',
cell_editors: [
{ type: 'none' },
{ type: 'input', attributes: [['title', 'First name and last name']] },
{ type: 'input', attributes: [['title', 'email address']] },
{ type: 'none' },
{ type: 'input', attributes: [['maxLength', 10], ['title', '10 digits max.\n numbers with 2 decimal places only']], css: 'alignRight' },
{ type: 'boolean' }
],
actions: {
'update': {
uri: 'php/employeeUpdate.php', submit_method: 'form', form_method: 'POST',
param_names: ['id', 'name', 'email', 'startdate', 'salary', 'active'],
on_after_submit: function (o) {
ajax.isRefreshed = false;
// selected row index is stored
ajax.activeRowIndex = o.Selection.GetActiveRow().rowIndex;
// rows are loaded
ajax.loadContent();
}
},
'insert': {
uri: 'php/employeeInsert.php', submit_method: 'form', form_method: 'POST',
param_names: ['id', 'name', 'email', 'startdate', 'salary', 'active'],
default_record: ['', 'Employee name...', 'employee@email.com', '2011-01-01', '0.00', ' '],
on_after_submit: function (o) {
ajax.isRefreshed = false;
// selected row index is stored
ajax.activeRowIndex = o.Selection.GetActiveRow().rowIndex;
// rows are loaded
ajax.loadContent();
}
},
'delete': {
uri: 'php/script.deleteEmployee.php', submit_method: 'script', bulk_delete: false,
param_names: ['id'],
on_after_submit: function (o) {
ajax.isRefreshed = false;
// rows are loaded
ajax.loadContent();
}
}
},
//Callback triggered when a new row element is added
on_added_dom_row: function (o) {
//TF total rows nb needs to be re-calculated
tf.nbRows = tf.GetRowsNb();
},
//Data validation delegate, the ezEditTable script does not provide data validation tools
validate_modified_value: function (o, colIndex, oldVal, newVal, cell, editor) {
if (colIndex == 2 && oldVal != newVal) {
if (!et_ValidateEmail(newVal)) {
alert('Please insert a valid email!');
return false;
} else return true;
}
else if (colIndex == 4 && oldVal != newVal) {
if (!et_IsNumber(newVal)) {
alert('Please insert a valid number with 2 decimal places!');
return false;
} else return true;
}
else return true;
}
}
}
//jQuery AJAX
var ajax = {
isRefreshed: false,
loadContent: function () {
if (this.isRefreshed) { return; }
// loading message
$('#loadLabel').html('Loading data...');
// AJAX call
this.load('php/getEmployees.php');
},
load: function(uri){
var xhr = $.ajax({
url: uri + '?r='+ new Date().getTime(),
context: $('#demo tbody'),
type: 'GET',
dataType: 'html'
})
.done(function (data) {
// inject html table rows
this.html(data);
// loading message is cleared
$('#loadLabel').html('');
// filters need to be refreshed after
// table content is loaded
if (tf) {
// temp hack to make the refresh filters feature
// work correctly when paging is on. This will be
// fixed in next release
if(tf.paging){ tf.paging = false; }
// total rows number is recalculated
tf.nbRows = tf.GetRowsNb();
// filters are refreshed
tf.RefreshFiltersGrid();
// paging is reset if on
if(!tf.paging && tf_Id('paging').checked){
tf.RemovePaging();
tf.paging = true;
tf.SetPaging();
}
// reapply alternating backgrounds
tf.SetAlternateRows();
// ezEditTable created, modified, deleted rows arrays are reset
tf.ezEditTable.Editable.modifiedRows = [];
tf.ezEditTable.Editable.addedRows = [];
tf.ezEditTable.Editable.deletedRows = [];
//Selection is reapplied if any
if(ajax.activeRowIndex){
tf.ezEditTable.Selection.SelectRowByIndex(ajax.activeRowIndex);
}
//Page is also reselected
if(tf.paging && tf.currentPageNb > 0){
tf.SetPage(tf.currentPageNb);
}
ajax.isRefreshed = true;
} else {
// first time rows are loaded filters are instantiated
tf = new TF('demo', config);
tf.Init();
}
})
.fail(function(jqXHR, textStatus){
$('#loadLabel').html(textStatus);
this.html('Ouups an error occured! Data could not be loaded!');
});
}
};
//Validation functions for cell editors
function et_ValidateEmail(email) {
var reg = /^([A-Za-z0-9_\-\.])+\@([A-Za-z0-9_\-\.])+\.([A-Za-z]{2,4})$/;
return reg.test(email);
}
function et_IsNumber(val) {
var isNumber = /^\d+\.\d{2}$/;
return isNumber.test(val);
}
var tf; //global TF instance
$(function(){
// table rows are loaded when page is ready
ajax.loadContent();
});
PHP page serving the HTML table rows:
connect_db(); $query = mysql_query( "SELECT ID, NAME, EMAIL, STARTDATE, SALARY, ACTIVE" . " FROM " .TBL_DEMO_EMPLOYEE. " WHERE DELETED = 0" . " ORDER BY ID DESC LIMIT 0, 30" ); while ( $row = mysql_fetch_row( $query )){ $id = $row [0]; $name = $row [1]; $email = $row [2]; $startdate = $row [3]; $salary = $row [4]; $active = $row [5]; $checked = "" ; if ( $active == 1) $checked = "checked=\"checked\"" ; echo ( "\n\t" ); echo ( "" . $id . "\n\t" ); echo ( "" . $name . "\n\t" ); echo ( "" . $email . "\n\t" ); echo ( "" . $startdate . "\n\t" ); echo ( "" . $salary . "\n\t" ); echo ( "<input type=" \"checkbox\" " " . $checked . "=" ">\n" ); echo ( "\n\t" ); } close_db(); connect_db();
$query = mysql_query("SELECT ID, NAME, EMAIL, STARTDATE, SALARY, ACTIVE".
" FROM ".TBL_DEMO_EMPLOYEE." WHERE DELETED = 0".
" ORDER BY ID DESC LIMIT 0, 30");
while($row = mysql_fetch_row($query)){
$id = $row[0];
$name = $row[1];
$email = $row[2];
$startdate = $row[3];
$salary = $row[4];
$active = $row[5];
$checked = "";
if($active == 1) $checked = "checked=\"checked\"";
echo("\n\t");
echo("".$id."\n\t");
echo("".$name."\n\t");
echo("".$email."\n\t");
echo("".$startdate."\n\t");
echo("".$salary."\n\t");
echo(" \n");
echo("\n\t" );
}
close_db();
Insert, update and delete operations are served by the same server-side
pages detailed in this demo: editable-grid.php