Thursday, August 15, 2013

Editable HTML Grid View with JQuery


Most of the times we need to have editable grid views in html pages where the user can view the existing records in non-editable mode and if they need to edit data, with a simple click event they can convert the same row of the grid to the editable mode. For example following is a record of the grid view which already exists. 



 Now the user needs to edit the same row without prompting into another section or popup. Which means the user needs the inline edit option. Then it would be appear as follows.


You may note that the ‘edit’ icon has been changed to ‘save’ in the Action column. Also the idea of having ‘Add Row’ button is to add a new row to the grid as the name saying itself. Below is a view with an existing record and newly added row by clicking the ‘Add Row’ button.


In each row, at the end I’m having a ‘delete’ icon so that any row can be deleted independently. Tired with explaining the functionalities, also I know that you are tired with reading :D. It’s time to move to the implementation.

Table
<table id="editablegrid" style="width:100%">
  <thead>
      <th>Action</th>
      <th>Date</th>
      <th>Link</th>
      <th>Currency Type</th>
      <th>Value</th>
      <th>Verified</th>
  </thead>
  <tbody></tbody>
 </table>    

 <input id="btnAddrow" type="button" value="Add Row" onclick="Add(null)" />
Dynamic Functionalities with JQuery
First, lets's have a global variable to hold the number of rows deleted. Later you may get why doing this.
var deletedRows = 0;    //hold the number of rows deleted.
Add : Here I'm assuming there can be some existing records as well. So the Add function will be called from two different places; for each existing record(probably from the $(document).ready), from the Add button click event. To handle both, I'm using the parameter that is passed to the function as follows. Also note that my
instance
object is having some fields as it's using the dot(.) notation.
function Add(instance){
  var rowCount = $('#editablegrid tr').length;
  var delRows = window.deletedRows;
            
  if(instance == null)
  {
     $("#editablegrid tbody").append(
     "<tr id='sdr"+rowCount+"_"+delRows+"'>"+
     "<td><img id='btnSave"+rowCount+"_"+delRows+"' src='images/save.png' /></td>"+
     "<td><input id='date"+rowCount+"_"+delRows+"' type='text' /></td>"+
     "<td><input type='text' /></td>"+
     "<td><select id='currency"+rowCount+"_"+delRows+"'></select></td>"+
     "<td ><input type='text' /></td>"+
     "<td><input type='checkbox'/></td>"+
     "<td><img id='btnDelete"+rowCount+"_"+delRows+"' src='images/delete.png' /></td>"+             
     "</tr>");
                                        
     for(var i=0;i<currencyTypes.length;i++)
     {
       $('#currency'+rowCount+'_'+delRows).append($("<option></option>")
       .attr("value",currencyTypes[i])
       .text(currencyTypes[i]));
     }
                    
     $("#btnSave"+rowCount+"_"+delRows).bind("click", Save);
   }
   else
   {
     var verifiedVal = (instance.Verified == true)?1:0;
     var verifiedChecked = (instance.Verified == true)?'checked="checked"':'';

     $("#editablegrid tbody").append(
     "<tr id='sdr"+rowCount+"_"+delRows+"'>"+
     "<td><img id='btnEdit"+rowCount+"_"+delRows+"' src='images/edit.png' /></td>"+
     "<td>"+instance.date+"</td>"+
     "<td title='"+instance.link+"'>"+instance.link+"</td>"+
     "<td>"+instance.currency+"</td>"+
     "<td >"+instance.number+"</td>"+
     "<td><input type='checkbox' value='"+verifiedVal+"' "+verifiedChecked+"/></td>"+
     "<td><img id='btnDelete"+rowCount+"_"+delRows+"' src='images/delete.png' /></td>"+     
     "</tr>");
                    
     $("#btnEdit"+rowCount+"_"+delRows).bind("click", Edit);
   }                                  
   $("#btnDelete"+rowCount+"_"+delRows).bind("click", Delete);
 };


Save:
function Save(){    
 var par = $(this).parent().parent(); //tr
 var rowIndex = par.attr('id').substring(3);
 var delRows = window.deletedRows;
 var sowDetailRecord = [];
            
 var tdAction = par.children("td:nth-child(1)");
 var tdDate = par.children("td:nth-child(2)");
 var tdLink = par.children("td:nth-child(3)");
 var tdCurrency = par.children("td:nth-child(4)");
 var tdValue = par.children("td:nth-child(5)");
 var tdVerified = par.children("td:nth-child(6)");
 var tdHidden = par.children("td:nth-child(8)");
            
 tdAction.html("<img id='btnEdit"+rowIndex+"_"+delRows+"' src='images/edit.png' class='btnEdit'/>");
 tdDate.html(tdDate.children("input[type=text]").val());
 tdLink.html(tdLink.children("input[type=text]").val());
 tdCurrency.html(tdCurrency.children("select").find(":selected").text());
 tdValue.html(formatCurrency(tdValue.children("input[type=text]").val()));            
        
 $("#btnEdit"+rowIndex+"_"+delRows).bind("click", Edit);

};


EDIT: here for the dropdown I'm trying to show currency types. Please note that the
currencyType
array need to be declared first. Since I'm showing the main functionalities of the grid view thought to remove that. Make sure you initialize that if you are going to have a dropdown.
function Edit(){            
 var par = $(this).parent().parent(); //tr
 var rowIndex = par.attr('id').substring(3);
 var delRows = window.deletedRows;
            
 var tdAction = par.children("td:nth-child(1)");
 var tdDate = par.children("td:nth-child(2)");
 var tdLink = par.children("td:nth-child(3)");
 var tdCurrency = par.children("td:nth-child(4)");
 var tdValue = par.children("td:nth-child(5)");
            
 var existingCurrencyType = tdCurrency.html();
                    
 tdAction.html("<img id='btnSave"+rowIndex+"_"+delRows+"' src='images/save.png' />");
 tdDate.html("<input type='text' id='txtDate"+rowIndex+"_"+delRows+"' value='"+tdDate.html()+"'/>");
 tdLink.html("<input type='text' id='txtLink' value='"+tdLink.html()+"'/>");
 tdCurrency.html("<select id='currency"+rowIndex+"_"+delRows+"' style='width:100%'></select>");
 tdValue.html("<input type='text' id='txtValue' value='"+tdValue.html()+"' />");
        
 for(var i=0;i<currencyTypes.length;i++)
 {
   $('#currency'+rowIndex+'_'+delRows).append($("<option></option>")
     .attr("value",currencyTypes[i])
     .text(currencyTypes[i]));
 }
        
 $('#currency'+rowIndex+'_'+delRows).find('option').each( function() {
  var $this = $(this);
  if($this.text() == existingCurrencyType) {
    $this.attr('selected','selected');
    return false;
  }
 });

 $("#btnSave"+rowIndex+"_"+delRows).bind("click", Save);
};


DELETE:
function Delete(){
            var par = $(this).parent().parent(); //tr
            window.deletedRows++;
            par.remove();
        };
Hope you can guess that deletedRowsvariable is maintaining here to bind the id of each element correctly when a deletion is happen. Cheers!