Start a new topic

Salesforce OnClick JavaScript Button alternative

The main way to start/execute Apex Batch processes is creating an OnClick JavaScript Button.


There are two issues with this:

  • The apex process (WebService) is executed synchronously (i.e. UI is blocked)
  • The Javascript is executed in the Salesforce DOM, which is now on the "don't do" list when you want to pass the Salesforce Security Review
There are few alternatives
  • Lightning Quick Actions - the only option in Lightning - but it is not available in the Classic UI.
  • Visualforce page - but you cannot pass parameters, although you can access the Id

So for a 6-8 line script, you need to create 3 files (page, controller, test) - not a great option if you have about 20 buttons to replace.


Our solution involves converting the OnClick Javascript to URL and creating a Visualforce Page with Controller to handle all buttons.


The standard alert user interface for OnClick buttons - example here is a process to re-calculate the Project Actual amounts after e.g. changing the resource prices:


TTXehVzk67kp5QbRPoSZiZzyQmlUNr2EoA.png


I think, we improved a bit for the new UI:

uw-qwzhF4UMqmjl1pL6uJv_elZ_gspIVtw.png

Here the details + code:


The suggested code based on the Salesforce Cookbook for Apex Buttons:

 

{!REQUIRESCRIPT("/soap/ajax/36.0/connection.js")} 
{!REQUIRESCRIPT("/soap/ajax/36.0/apex.js")} 

var result = sforce.apex.execute("accorto.ProjectLineNS", "updateProject", 
{projectId: "{!accorto__Project__c.Id}"}); 
alert(result[0]); 
window.location.reload(true);

  

The replacement Button URL - calling the Visualforce ApexStart with the parameters

 

/apex/ApexStart?a=accorto.ProjectLineNS&m=updateProject
&n=projectId&v={!accorto__Project__c.Id}
&r=/{!accorto__Project__c.Id}&q={!URLENCODE('Recalculate Project Actuals')}

 

The ApexStart.page:

  

<apex:page controller="ApexStartController" title="Start Process" 
    showHeader="false" standardStylesheets="false" docType="html-5.0" applyHtmlTag="false" applyBodyTag="false" id="a">
<html xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<head>
  <apex:includeScript value="/soap/ajax/36.0/connection.js"/>
  <apex:includeScript value="/soap/ajax/36.0/apex.js"/>
  <apex:stylesheet value="{!URLFOR($Resource.SLDS214, 'assets/styles/salesforce-lightning-design-system.css')}" />
</head>
<body>
 <apex:form id="f">
  <div class="slds-modal__container">
    <div class="slds-modal__header slds-theme--info">
      <div class="slds-media__figure">
          <svg aria-hidden="true" class="slds-icon slds-icon--small">
              <use xlink:href="{!URLFOR($Resource.SLDS214, '/assets/icons/utility-sprite/svg/symbols.svg#process')}"></use>
          </svg>
        </div>
      <h2 class="slds-text-heading--medium">{!q}</h2>
    </div>

    <div class="slds-modal__content slds-p-around--medium" style="min-height:80px">
        <apex:outputPanel id="result" layout="block" rendered="{!execScript}">
            <div class="slds-spinner--brand slds-spinner slds-spinner--small" role="alert">
                <span class="slds-assistive-text">Processing</span>
                <div class="slds-spinner__dot-a"></div>
                <div class="slds-spinner__dot-b"></div>
            </div>
        </apex:outputPanel>
    </div>
    
    <div class="slds-modal__footer slds-theme--default">
      <apex:commandButton value="Cancel" action="{!doCancel}" styleClass="slds-button slds-button--neutral"/>
      <apex:commandButton value="Start" action="{!doStart}" styleClass="slds-button slds-button--brand"/>
    </div>
  </div>

  <apex:outputPanel id="script" layout="none" rendered="{!execScript}">
  <script>
    sforce.connection.sessionId="{!GETSESSIONID()}";    
    // logistics    
    var resultId = "{!$Component.result}";
    var resultDiv = document.getElementById(resultId);
    var callback = {onSuccess: handleSuccess, onFailure: handleFailure};
    function handleSuccess(result, source) {
        resultDiv.innerText = result;
    }
    function handleFailure(error, source) {
        resultDiv.innerText = error;
    }
    // ApexCall
    var params = {};
    params["{!n}"] = "{!v}";
    var result = sforce.apex.execute ("{!c}", "{!m}", params, callback);
  </script>    
  </apex:outputPanel>
 </apex:form>
</body>
</html>
</apex:page>

  

The ApexStartController.cls:

 

public with sharing class ApexStartController {
    public ApexStartController() {
        params = System.currentPageReference().getParameters();
        // System.debug(params);
        execScript = false;
    }
    /** Parameter */
    public Map<String,String> params { get; set; }
    /** Question */
    public String q {
        get {
        	if (q == null) {
        		q = params.get('q');
        		if (q != null)
        			q = EncodingUtil.urlDecode(q, 'UTF-8');
        	}
        	return q;
        }
        set; 
    }
    /** Apex Class */
    public String c {
        get { return params.get('a'); }
        set; 
    }
    /** Apex Method */
    public String m {
        get { return params.get('m'); }
        set; 
    }
    /** Parameter Name */
    public String n {
        get { return params.get('n'); }
        set; 
    }
    /** Parameter Value */
    public String v {
        get { return params.get('v'); }
        set; 
    }

    public Boolean execScript { get; set; }
    
    public PageReference doStart() {
        execScript = true;
        return null;
    }

    public PageReference doCancel() {
        String r = params.get('r');
        if (r != null && r.length() > 0)
            return new PageReference(r);
        return Page.TEItemTimesheet;
    }
}

 

Advantages:

  • looks nicer
  • shows Spinner while executing the Apex WebService
  • easy conversion from JavaScript to URL with minimum effort


The disadvantage of this approach

  • the URL button opens another window which needs to be closed explicitly
  • user needs to refresh/requery explicitly to see the updated values in the Salesforce UI


Unfortunately - at this point - there are no alternatives for List Buttons - neither for Lightning nor for Classic - as the function GETRECORDIDS() is only working in the OnClick Javascript context.



Hey,

This seems interesting alternative to JS buttons. I want to try this but looks like 'SLDS214' is missing from code provided above. Please provide CSS too so that I can try it.

Thanks,
Jatin.

 

Thanks Jatin - it's the standard SLDS distribution - with the Winter release, you should just need: <apex:slds /> 

Login to post a comment