[SalesForce] Inline editing doesn’t update last edited value without control loosing focus first

When I double click on the output field and try to change the field value. The updated value is not getting saved when i directly click on save button.

Instead, In order to save record I need to click on screen first and then save. In this case record gets saved.

But I don't want to use second option as it is not efficient.

Can you all please suggest me a solution for this?

<apex:page standardController="Employee__c" extensions="HRMSController" action="{!checkSession}" showHeader="false" sidebar="false" tabStyle="Account" docType="html-5.0"> 
<link rel="stylesheet" href="//code.jquery.com/ui/1.11.2/themes/smoothness/jquery-ui.css" />
<script src="//code.jquery.com/jquery-1.10.2.js"></script>
<script src="//code.jquery.com/ui/1.11.2/jquery-ui.js"></script>    
<script>
    j$ = jQuery.noConflict();
    function phonenumber(inputtxt){
        var phoneno = /^\d{10}$/;
        if(inputtxt.value.match(phoneno)){
            return true;
        }else{
            alert("Enter 10 digit phone No.");
            j$('input[name$="mobileid"]').val('').focus();
            return false;
        }
    }    
</script>    
<apex:stylesheet value="{!$Resource.HRMSStyle}"/>
<style>
    #menu{
    float:none !important;
    }
    .customTable{
    margin-left:35px !important;
    width: 75% !important;
    }
    .pushbtn{
    margin-left:35px !important;
    } 
    .apexp .bPageBlock .detailList { width: 94%; }
</style>
<apex:include pageName="HRMSHeader"/>
<apex:form enctype="multipart/form-data" html-novalidate="novalidate" >

    <apex:pageMessages ></apex:pageMessages>         
    <apex:pageBlock >
        <apex:pageBlockSection >
            <apex:commandButton value="Add Candidate" action="{!hideMethod}" styleClass="pushbtn" />
        </apex:pageBlockSection>
    </apex:pageBlock>


    <apex:outputPanel >
        <apex:pageBlock >        
            <apex:pageBlockButtons location="Bottom" >
                <apex:commandButton value="Save" action="{!saveMethod1}" styleClass="pushbtn" onclick="phonenumber()"  />                       
                 <apex:commandButton value="Cancel" immediate="true" styleClass="pushbtn"/>
            </apex:pageBlockButtons>
            <br/>
            <div class="pageHeaderHRMS" style="padding-left: 2% !important; margin-left: 33px; width: 75%;">Details of Referral Employee</div>
            <br/> 
            <apex:pageBlockSection columns="2" collapsible="false" >    
                <apex:inputField value="{!cand.Name}" required="true" />
                <apex:inputField value="{!cand.Experience__c}" required="true" /> 
                <apex:inputField value="{!cand.Mobile_No__c}" required="true" id="mobileid" onchange="phonenumber(this)"/> 
                <apex:inputField value="{!cand.Email_ID__c}" required="true" />     
                <apex:inputField value="{!cand.Technology__c}" style="margin-right: 36px;margin-left: -3px;" /> 
                <apex:inputField value="{!cand.Location__c}" required="true" />
                <apex:outputLabel value="Upload Resume" for="fileToUpload" style="margin-left:550px;margin-left: 106px;"></apex:outputLabel>        
                <apex:inputFile required="true" style="width:100%;margin-left: -430px;" id="fileToUpload" value="{!myAttachment.Body}" filename="{!myAttachment.Name}" contentType="contentType" />             
          </apex:pageBlockSection>
        </apex:pageBlock>
    </apex:outputPanel>

    <apex:outputPanel >
        <apex:pageBlock >
           <apex:pageBlockTable value="{!Candidate}" var="cand" styleClass="customTable">
            <apex:column headerValue="Action">
                  <apex:commandLink style="color:#72d4ca;" value="Delete" action="{!deleteRecord}">
                      <apex:param name="ruleID" value="{!cand.id}"/>
                  </apex:commandLink> &nbsp;|&nbsp;
                  <apex:commandLink style="color:#72d4ca;" value="Save"  action="{!EmpReferralSave}"> </apex:commandLink>

            </apex:column>       
            <apex:column headerValue="Name" value="{!cand.Name}"></apex:column>   
            <apex:column headerValue="Email" value="{!cand.Email_ID__c}"></apex:column>
            <apex:column headerValue="CreatedDate" value="{!cand.CreatedDate}"></apex:column>
            <apex:column headerValue="Technology" value="{!cand.Technology__c}"></apex:column>                  
             <apex:inlineEditSupport event="ondblClick" />  
            </apex:pageBlockTable>
        </apex:pageBlock>        
    </apex:outputPanel>

</apex:form>
</apex:page>

Controller:

 // Method for showing Referral Employee Form defined on "Add candidate" button
Public void hideMethod(){
   if (showflag == false){
     showtb1 = true;
   }else{
        showflag = false;
        showtb1 = true;
        showtb2 = false;
   }
}
// Employee Referral - Method fires on Save button
Public pagereference saveMethod1(){    
   showtb2=true;
   showtb1=false;
   showflag = true;
   PageReference pageRef=HRMSUtilClass.EmployeeReferralSave(cand,myAttachment,userid,isSaved,contentType,showflag,showtb2,showtb1,Candidate);
   Candidate = [Select id, Name, CreatedDate, Email_ID__c, Technology__c from Candidate__c WHERE Employee__c = : userid order by Id DESC];//Status__c, Employee__c, Experience__c


   return pageRef;       
}
// Cancel button to hide Referral Employee Form defined on "Cancel" Button
public PageReference cancelemployeeref(){ 
    PageReference pageRef = HRMSUtilClass.EmployeeReferralCancel(userid,showflag,showtb2,showtb1);
    return pageRef;
}

// Method to Delete Record from Candidate List
public PageReference deleteRecord(){
    ID rid = ApexPages.currentPage().getParameters().get('ruleID');
    PageReference pageRef = HRMSUtilClass.EmployeeReferralDeleteRecord(userid,rid);
    for(integer i=0;i<Candidate.size();i++)
    {
        if(Candidate[i].Id == rid ){

           Candidate.remove(i);
           break;
        }



    }


    pageRef.setRedirect(false);   
    return pageRef;
 }
// Method to Save Record from Candidate List
Public PageReference EmpReferralSave(){

    showtb1 = false;
    showtb2 = true;  

    PageReference pageRef = HRMSUtilClass.EmployeeReferralUpdate(Candidate,userid);
    return pageRef;
 }

Best Answer

If Kaw's solution works, I would prefer his - my approach is way more complicated. At the time I did it, I found it necessary and considered this is a bug in Visualforce.

Basically I found two issues:

  • last edited value won't be saved: if the focus does not leave the input control (meaning it's still in editmode and the input controls are still visible), you have to click first somewhere else so that the input is loosing focus. Then the input controls will be hidden and work fine, but if you keep the focus on the control, the last field edited won't update correctly.
  • single-click event preselects text: if you change the event form onDblClick (default) to onClick, the text will be all preselected after you click into it and the controls appears. Even worse, the text is very hard to get deselected - simple clicks doesn't help. Need to move cursor with Keyboard, which really sucks.

The following is I distilled it out of a bigger Visulaforce fixing library but I hope the concepts will still become clear. I put JS and VF together for the sake of simplicity.

SFSEInlineEdit.cls

public class SFSEInlineEdit { 
    public list<sobject>            listItems               { get; set; }
    public list<wrap>               listWraps               { get; set; }
    public class wrap {
        public boolean              checked                 { get; set; }
        public sobject              item                    { get; set; }
    }
    public void listInit() {
        this.listUpdateWraps();
    }
    public PageReference listSave() {
        update listItems;
        this.listUpdateWraps();
        return null; 
    }
    public void listUpdateWraps() {
        this.listItems = Database.query( 'select Type, Name from Account' );
        this.listWraps = new list<wrap>();
        for(sobject item : this.listItems) {
            wrap wrap           = new wrap();
            wrap.item           = item;
            this.listWraps.add(wrap);
        }
    }
}

SFSEInlineEdit.page

<apex:page controller="SFSEInlineEdit" action="{!listInit}">  
    <apex:includeScript value="{!$Resource.jquery}"/>
    <script> 
        page = {
            afterUpdate: function() { var self = this; // this function should be part of a javascript tag which is re-renderd by action-functions to keep inline-editing fixed.
                self.inlineEdit.applyFix(); 
            },  
            inlineEdit: {
                applyFix: function() { var self = this;
                    $("form .inlineEditWrite").each(function(){
                        if($(this).find('.elfCellcheckImg').size()==0) {
                            $(this).attr("onclick",$(this).attr("onclick")+"; page.inlineEdit.activate(); ");
                            $(this).attr("onmouseover","return false;");
                            $(this).attr("onmouseout","return false;");
                        }
                    });
                },
                syncAllElements: function() { var self = this;
                    console.log("[elfPage] syncing inlineEdit fields...");
                    $('.inlineEditDiv input').each(function(){
                        console.log(this.id+" : "+$(this).val());
                        var c = sfdcPage.getInlineEditData(this.id);
                        c.closeCurrentField(null, !1);
                    });
                },
                activate: function() { var self = this;
                    $(".inlineEditWrite input").click( function(){ return false; });
                    $(".inlineEditWrite input").attr("onchange"," page.inlineEdit.syncAllElements();  "); 
                    $(".inlineEditWrite select").click( function(){ return false; });
                },
            },
        };
    </script>
    <apex:form id="listForm">
        <apex:pageBlock title="Test" >
            <apex:pageBlockButtons >
                <apex:commandButton value="Save" action="{!listSave}" rerender="listForm" /></apex:pageBlockButtons>
            <apex:pageBlockTable value="{!listWraps}" var="wrap" id="itemTable">
                <apex:column width="25px">
                    <apex:inputCheckbox value="{!wrap.checked}" styleClass="elfItemCheckbox"/>
                </apex:column>
                <apex:column width="100%">
                    <apex:outputField  value="{!wrap.item['Name']}"/>
                </apex:column>
                <apex:column width="1" >
                    <apex:outputField  value="{!wrap.item['Type']}"/>
                </apex:column>
                <apex:inlineEditSupport event="onClick" showOnEdit="saveButton,cancelButton" hideOnEdit="editButton" />
            </apex:pageBlockTable>
        </apex:pageBlock>
        <script> // this script is placed here to be re-executed after ajax may have re-rendered the form
            page.afterUpdate();
        </script>
    </apex:form>
 </apex:page>

Reproduction

  • Add these two files to a DE.
  • Put jquery in a StaticResource called jquery.
  • go to apex/SFSEInlineEdit in your browser
  • see the patched behavior

Play with line 52 in the VF page and disable/enable it and compare Salesforce Standard vs. Patched behavior.

page.afterUpdate();

In the unpatched mode invoke the issues

  • Click on an account name to engage Inline Edit ==> selection issue
  • Change Name and don't leave the edit, just click Save ==> data loss

enter image description here

Related Topic