Easy To Write In Apex, But Not In Javascript? Use Javascript Remoting!

Thursday, June 9, 2011

New in the Summer '11 release of Salesforce.com is the GA of the shiny new Javascript Remoting. To quickly define it, Salesforce now provides the ability for you to write Javascript code on a Visualforce page that references a static method on that page's controller.

Now, there has always been a way for a Visualforce page to talk to its controller, by way of VF's actionFunction component as one among other built-in AJAX functionality, but Javascript remoting is a bit different. As its doc page tells about the differences between the two: Javascript remoting allows you to pass parameters to an Apex method and allows you to specify a Javascript callback function, whereas the actionFunction component allows you to specify rerender targets on the page and submits the page's entire form back to the controller. By communicating with the controller with the former, a light-weight JSON object is passed back and forth, but when using actionFunction, the page's entire form is serialized, sent across the wire, and deserialized again, which takes a significant amount of time. One is not inherently better than the other, but they each have their own use cases. This post is about the first use case that I found for JS remoting.

Problem

Challenge: Dynamically update the End Date when changing either Work Days or Start Date.
After estimating the time and dollar cost of a project, our process requires us to log our estimates into our Salesforce org. I was the lucky one chosen to implement the SF-side of the solution. I like to provide a friendly and intuitive user experience, so I wanted to make it very Ajax-y. More specifically, I wanted a way to  instantly update dependent values when the user enters the controlling value. This is easy with jQuery when it's just numbers and sums, but when it comes to computing dates, Javascript's client-side capabilities falter (unless a JS pro can inform me otherwise).

Solution

I had already solved this problem in Apex, for calculating this value when the record is submitted, by creating a method called getEndDateAfterWorkdays, which takes a Date startDate and an Integer workDays as arguments. I'm sure it's possible to do in Javascript, but I'm not a pro with the language and I didn't really want to duplicate the logic there (see DRY), so I looked for other solutions. Luckily for me, the Summer '11 release was just around the corner, which would allow me to use JS remoting to call the method I already defined in Apex!

The Code

Here's how it works.

1) Attach an onChange Javascript event listener to the Work Days and Start Date input fields on the VF page that will call the Javascript remoting function.

$j(".duration").live("keyup", function(){ updateEndDate(); });
$j(".startDate").live("change", function(){ updateEndDate(); });


//using javascript remoting, send the startDate and the duration to the server, get the endDate back
function updateEndDate() {
$j(".startDate").each(function() {
//Get the necessary info from each row in the list.
var startDate = $j(this).parent().parent().parent().find(".startDate").val();
var duration = $j(this).parent().parent().parent().find(".duration").val();
var endDateId = $j(this).parent().parent().parent().find(".endDate").attr("id");
//Remotely call the controller method.
CtlrEstimateEdit.getEndDateAfterWorkdays( startDate, duration, endDateId, function(result, event) {
//This callback function doesn't remember the row from which it was called.
// My solution: pass the Id of the destination element into the function and pass it back into this callback.
var resultArray = result.split(",");
var resultEndDate = resultArray[0];
var resultSpanToInsertId = resultArray[1];
if (event.status) {//if successful
var spanToInsertIdEsc = esc(resultSpanToInsertId);
$j(spanToInsertIdEsc).html(resultEndDate);
}
});
});
}


2) Add the remoting method to the controller for the Javascript to hit.

@RemoteAction
global static String getEndDateAfterWorkdays(String pStartDate, Integer pWorkDays, String pEndDateIdToPass) {
Date startDate = Date.parse(pStartDate);
Date endDate = startDate;
Integer durationInCalendarDays = HlprLaborEstimateMasterTrigger.workDaysDurationToCalendarDaysDuration(pWorkDays - 1, startDate);//Minus 1 to start working that day
endDate = startDate.addDays(durationInCalendarDays);
//now convert back to a string to send back to the javascript
DateTime endDateTime = DateTime.newInstance(endDate, Time.newInstance(0, 0, 0, 0));
String endDateToReturn = endDateTime.format('MM/dd/yyyy');
//add the endDateId of the span to which to write this new value. I couldn't figure out another way but to push this value through here
String endDatePlusSpanIdToReturn = endDateToReturn + ',' + pEndDateIdToPass;
return endDatePlusSpanIdToReturn;
}


That's it, really, unless I forgot to paste some code here. (Let me know if I did, please.)

Besides enforcing the DRY coding principle, what other use cases have you found for using Javascript remoting?

1 comment: