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!

Wednesday, January 23, 2013

Adding New Fields to an Existing Report Type in Salesforce - SFDC

In SFDC, one of very common complains from the new users is "Some of my fields are disappeared in Reports in SFDC". This is simply because you have not added those fields to the particular report type.

Just go to your report type and add the required fields and save. Follow these instructions.

  • Click Your Name|Setup|Create|Report Types
  • If the introductory message is there, click on Continue
  • Click on the Report Type which you need to edit
  • Click on Edit Layout in the Fields Available for Reports section
  • In the right hand side you can see all the fields of the objects in your report type.
  • Just select the Object from the drop down list in the View section.
  • Already added fields will be appeared as disabled
  • Select the Fields that you need to add and drop them into the section that you need(If there is no any section for your object, create a new section by clicking Create New Section button at the bottom).
  • After selecting all the required fields save the report type.

That's it. Cheers!!!

Last Modified Date in Salesforce - SFDC

This may be a somewhat silly thing to say to the outer world. But I thought to write this post, assuming that there is at least one people who is suffering from this problem ;)

 In SFDC for all the Standard objects as well as for the custom objects, there is a standard field called LastModifiedDate which is invisible in the field list. But if we are accessing these objects through API calls, then we can replicate this field as well into SQL Database or similar. There we can see that, the field is in DataTime format.

Even though the field is invisible in the field list, when we run a report on SFDC we can see that that field is available in the left panel. Ok, now I'll turn into the problem that I faced and the workaround.

I wanted to check the last modified time of each record(let say Opportunity object). But when I ran a report what the last modified date was giving is only the last modified date(as the name is saying itself ;) ). But I got to know that this field is in the type of DataTime. So, with following workaround, I could able to get the DataTime format of this field.

  • Add a new field to the particular object. In my case, for the Opportunity object.
  • Click on Your Name|Setup|Customize|Opportunities|Fields
  • Under Opportunity Custom Fields & Relationships, click on New 
  • Select the Formula as the Data Type of the field 
  • Give a name to the field and select DataTime as the Return Type and click Next 
  • In the Insert Field section, select the Last Modified Date 
  • Now this is your formula.In the formula area it should only appear LastModifiedDate 
  • That's it. Click Next and Follow the formal instructions if any. 

Finally go back to your report type and add the newly added field there. If you unfamiliar with adding a new fields to existing report type, have a look at here. It should now show the DateTime format of the LastModifiedDate field.

Wednesday, January 16, 2013

Creating HTML elements dynamically with JavaScript

When we are developing web pages using markup languages like HTML, the web page will be appeared as we have already written with HTML. But what if we need to add some more HTML elements, add/edit some of properties of HTML elements even after rendering to browser? JavaScript comes into the game here. Below I have given a JavaScript function to add a row for an existing HTML table with some of HTML elements inside table cells.

function addRow(tableId){
   var table = document.getElementById(tableId);
   var row = table.insertRow(rowCount);

   var cell1 = row.insertCell(0);
   var element1 = document.createElement("label");
   element1.innerHTML = "Cell One-Label";
   cell1.appendChild(element1); 

   var cell2 = row.insertCell(1);
   var element2 = document.createElement("input");
   element2.type = "text";
   element2.value - "Cell Two-Input Text";

   cell2.appendChile(element2); 
   var cell3 = row.insertCell(2);
   var element3 = document.createElement("textarea");
   element3.value = "Cell Three-Text Area";
   cell3.appendChild(element3);
}

By calling this function(using a button click event, etc.) with the 'id' of the relevant table as an argument, we can add a label, input text and a textarea there.

Also when we need to set some attributes to be set dynamically, it also can be done. Below I'm explaining this by using the element1.

element1.setAttribute('id','myLabel'); 
element1.setAttribute("onChange",someFunction());
element1.setAttribute("onkeypress",someFunction());
element1.setAttribute("style","width:65px; text-align:right");

This way, we can add almost all the attributes for an HTML element dynamically.