Two Visualforce Pages: ActionFunction and JavaScript Remoting | Salesforce Developers Blog

We’ve seen a lot of attention about JavaScript Remoting while in developer preview (and if you need an introduction), and I’ll be going into more specific use cases in blog posts to come, but I thought it might be interesting to check out the same problem being solved in two completely different ways: the first being the standard Visualforce ActionFunction component, and the second using JavaScript Remoting.  We’ll need to pages and one controller.

The use case is a bit random, I was actually tinkering with some jQuery for a question posted on the boards, so I wanted something that would create a usable jQuery slider within a table created dynamically.  So here I’m creating a list of opportunities based on an account, and attaching a slider which can update the opportunity amount.  One button then updates all the current amounts in the system.

The controller code looks like this:

global with sharing class OpportunitySliderController {
      public List<Opportunity> opportunities {get; set;}
      public List<Account> accounts {get; set;}
      public Id accountId {get; set;}
      public OpportunitySliderController() {
      accounts = [SELECT Id, Name from Account];
}

public PageReference selectAccount() {
      opportunities = [SELECT Id, Name, Amount from Opportunity WHERE AccountId =: accountId];
      return null;
}

public PageReference updateOpportunities() {
      update opportunities;
      return null;
}

//REMOTE Functions
@RemoteAction
global static List<Opportunity> selectAccountRemote(string accountIdRemote) {
      List<Opportunity> opportunities = [SELECT Id, Name, Amount from Opportunity WHERE AccountId =: accountIdRemote];
      return opportunities;
}

@RemoteAction
global static Boolean updateOpportunity(string oppId, integer amount) {
      Opportunity o = [SELECT Id, Amount from Opportunity WHERE Id =: oppId];
      o.Amount = amount;
      update o;
      return true;
}

}


Unfortunately our syntax highlighter kept devouring our code, so I’ll have a link at the end for a package to easily download the controller and pages.  The controller contains all the code our dueling pages will need, but even here you can see the specific changes – sending IDs, not objects for instance.

The Visualforce page which uses actionFunction looks as you might expect.  After getting all our jQuery resources in order, we setup our actionFunction, and have a select to choose an Account:

    <apex:actionFunction name="selectAccount" action="{!selectAccount}" rerender="campaignTable" oncomplete="resetSliders()">
    <apex:param name="accountId" assignTo="{!accountId}" value="" />
    </apex:actionFunction>
    <select onchange="selectAccount(this.options[this.selectedIndex].value)">
    <OPTION>Select Campaign</OPTION>
    <apex:repeat value="{!accounts}" var="account"  >
    <OPTION Value="{!account.Id}">{!account.Name}</OPTION>
    </apex:repeat>
    </select>

Then we have some JavaScript to setup the sliders, which we can call on an oncomplete to reset them after a rerender event:

<script>
function resetSliders() {
<apex:repeat value="{!opportunities}" var="opp">
j$( "#opp{!opp.Id}" ).slider({
value:{!opp.Amount},
min: 0,
max: 1000000,
step: 1000,
slide: function( event, ui ) {
j$( "#opp{!opp.Id}" ).next("input").val(ui.value );
}
});
</apex:repeat>
}
</script>

Then a repeater to show the actual table, and a commandButton to update the results from the Controller.

<table> <apex:repeat value=”{!opportunities}” var=”opp”> <tr> <td>{!opp.Name}</td> <td>{!opp.Amount}</td> <td><div id=”opp{!opp.Id}”></div> <apex:inputText value=”{!opp.Amount}” /> </td> </tr> </apex:repeat> </table> <apex:commandButton rerender=”campaignTable” action=”{!updateOpportunities}” value=”Update” oncomplete=”resetSliders()” />

The Remoting version looks a lot different.  Now the idea here was to create a remoting solution which didn’t rely on a form tag to ferry data.  So here we have a rather large slice of JavaScript to setup our Sliders, and since we aren’t relying on Visualforce to embed ID’s for us, we string wrangle a table together which can then be used to send a request back to the server:

  <script> j$ = jQuery.noConflict(); var sliders; var opps; function showResult(result, event) { if(event.type == ‘exception’) { alert(event.message); } else { opps = result; sliders = new Array(); var innerHTML = ‘<table>’; for(i = 0; i < result.length; i++) { innerHTML += “<tr><td>”+result[i].Name+”</td>”; innerHTML += “<td id=’oppTD”+result[i].Id+”‘>”+result[i].Amount+”</td>”; innerHTML += “<td><div id=’opp”+result[i].Id+”‘ style=’width:100px’></div><input id=’oppInput”+result[i].Id+”‘ type=’text’ value='”+result[i].Amount+”‘ /></td>”; innerHTML += “</tr>”; } innerHTML += ‘</table>’ document.getElementById(‘tableRows’).innerHTML = innerHTML; for(var i = 0; i < result.length; i++) { j$( “#opp”+result[i].Id ).slider({ value:result[i].Amount, min: 0, max: 1000000, step: 1000, slide: function( event, ui ) { setSliderValues(); } }); } } } function setSliderValues() { for(i = 0; i < opps.length; i++) { document.getElementById(‘oppInput’+opps[i].Id).value = j$( “#opp”+opps[i].Id ).slider(“value”); } } function updateOpps() { for(i = 0; i < opps.length; i++) { OpportunitySliderController.updateOpportunity(opps[i].Id,document.getElementById(‘oppInput’+opps[i].Id).value,function() {}); document.getElementById(‘oppTD’+opps[i].Id).innerHTML = document.getElementById(‘oppInput’+opps[i].Id).value; } } </script>

The Visualforce itself is really just a repeater for the account list (which could have been done with remoting as well…) and then some empty divs to throw everything into.

<select onchange="OpportunitySliderController.selectAccountRemote(this.options[this.selectedIndex].value,showResult)">
<OPTION>Select Campaign</OPTION>
<apex:repeat value="{!accounts}" var="account"  >
<OPTION Value="{!account.Id}">{!account.Name}</OPTION>
</apex:repeat>
</select>
<div id="tableRows"></div>
<button onClick='updateOpps()'>Update</button>

Now the end behavior is nearly identical.  However, the Remoting page loads and reacts much faster.  And yet, the Remoting page also took more code to generate the table and handle the data in general.  This isn’t necessarily a case where I’m going to point to one or the other and proclaim either solution as the best example.  Quite the opposite – while this is an exercise just to highlight the specific difference between the two styles, what I’m seeing more from people utilizing Remoting is a hybridization of the two.  Where and how to use one tool over the other is a question left to the developer.  You’ll need to ask yourself, what’s the use case?  Do I have mobile users? Am I loading a lot of data that I need to see quickly?  Or is it simpler to just render a datatable and perhaps inject a few key Remote callbacks?

For a lot of applications, relying heavily on JavaScript may not be ideal, and the components you’re familiar with will continue to do the great work they’ve been doing.  In some instances, the lack of a viewstate, the ability to handle the responses as JSON, and integrating right into your custom JavaScript may be exactly what you need.

You can grab the package of the code above as a starting point to start mashing your ideas together.  You do have see the speed difference to feel some of the charm of Remoting.  It’s not that Visualforce was painfully slow, but there is a definite pause which Remoting doesn’t share.

If you’ve got your own examples of using Remoting with Visualforce, or still want to get into the developer preview (though all signs point to Remoting being GA with Summer ’11, so the wait shouldn’t be much longer) – send me a tweet.

Stay up to date with the latest news from the Salesforce Developers Blog

Subscribe