I’ve spoken about how to do this at a high level during Cloudstock London and there are hints at how it can be done but no formal documentation that I’ve found, so here we are 🙂
Quite simply JavaScript Remoting will transform Apex objects and classes (or collections of these types) into JavaScript objects for you. The opposite is true too but there are some rules you need to observe.
Apex Types to JavaScript Equivalents
This is the easier of type conversions in that you don’t have to really do anything to make it happen. The code below uses a custom class that I’ve defined but you can do the same with any sObject too. Let’s have a look at the code.
The Controller
public with sharing class RemotingObjectsController {
/* The remoting method simply instantiates a two custom types, puts
them into a list and then returns them. */
@RemoteAction
public static List<CustomClass> getClassInstances(){
List<CustomClass> classes = new List<CustomClass>();
CustomClass me = new CustomClass('Wes');
CustomClass you = new CustomClass('Champ');
classes.add(me);
classes.add(you);
return classes;
}
/* My custom type */
public class CustomClass{
public String firstName{get;set;}
CustomClass(String firstName){
this.firstName = firstName;
}
}
}
The Visualforce
<apex:page controller="RemotingObjectsController">
<script>
// Will hold our converted Apex data structures
var classInstances;
Visualforce.remoting.Manager.invokeAction(
'{!$RemoteAction.RemotingObjectsController.getClassInstances}',
function(result, event) {
// Put the results into a var for pedantries sake
classInstances = result;
console.log(classInstances);
// Assign the first element of the array to a local var
var me = classInstances[0];
// And now we can use the var in the "normal" JS way
var myName = me.firstName;
console.log(myName);
});
</script>
</apex:page>
The Output
JavaScript Types to Apex Equivalents
This is a little tricker, especially when it comes to sObjects. Note that the approach below works for classes and sObjects too.
The Visualforce Page
<apex:page controller="RemotingObjectsController">
<script>
/* Define a JavaScript Object that looks like an Account */
/* If you were using custom objects the name must include the "__c" */
function Account(){
/* Note the field names are case-sensitive! */
this.Id = null; /* set a value here if you need to update or delete */
this.Name = null;
this.Active__c = null; /* the field names must match the API names */
}
var acc1 = new Account();
acc1.Name = 'Tquila';
acc1.Active__c = 'Yes';
var acc2 = new Account();
acc2.Name = 'Apple';
acc2.Active__c = 'Yes';
var accounts = new Array(acc1, acc2);
Visualforce.remoting.Manager.invokeAction(
'{!$RemoteAction.RemotingObjectsController.insertAccounts}',
accounts,
function(result, event) {
console.log(result);
});
</script>
</apex:page>
The Controller
There is not much to the controller in this case.
public with sharing class RemotingObjectsController {
@RemoteAction
public static void insertAccounts(List<Account> accounts){
insert accounts;
}
}
Why is this cool?
Good question. If the Force.com Platform didn’t do this for you then we – the developer – would need to convert ours types explicitly on both the server-side and the client-side, and man-oh-man is that boring, error-prone work. Yet again the guys at salesforce.com have built in a convenience that saves us time and let’s us get on with the work of building cool apps.
Interesting post! I did not know Salesforce can do automatic type conversion. It’s also worth pointing out the awesome ‘apex:type’ key(sorta annotation) that you can use if your remote method parameter is an interface. More details in the docs here: http://www.salesforce.com/us/developer/docs/pages/Content/pages_js_remoting.htm
Cheers,
Anup
Reblogged this on Sutoprise Avenue, A SutoCom Source.
Hi nice post,
I have a question –
You talked about the object which are already defined in salesforce. I want to send a map to the remote method. Is there any trick. I am also trying with jquery serialize array to send the json data to the remote method but dont know its not working.
This will help you to understand what I want
https://sites.secure.force.com/success/servlet/rtaImage?eid=a1X30000000dkOn&feoid=00N30000006WEw0&refid=0EM300000016hdr
Thanks in advance.
I understand this is probably basic knowledge for most devs but I fought with this example for hours after realizing you forgot to add a comma in the most important part of the jQuery code.
Visualforce.remoting.Manager.invokeAction(
‘{!$RemoteAction.RemotingObjectsController.insertAccounts}’,
accounts, <———————-missing comma
function(result, event) {
console.log(result);
});
I definitely was able to use this to my benefit though so no worries 🙂
Just please add the comma
Whoops! Thanks for letting me know 🙂
Hi!
There is a far more readable notation for this call:
RemotingObjectsController.insertAccounts(accounts,function(result, event) {
console.log(result);
});
Cheers,
Detlef
Very true, that does work very well but it won’t work in managed packages unless you hardcode the prefix.
The platform does a great job of converting data to the Javascript equivalent in most cases… I’ve found that Date objects are a tricky exception. If you have a remoted method with SObjects as a parameter (or contained in a parameter, such as List), field values of type “date” are serialized as the integer milliseconds-since-epoch UTC value for midnight on that date, and appear to you as strings. Constructing a local Date from this naively can result in dates that are one day off. The following is my fix, interposed wherever in your (Javascript) code it makes sense:
var datefield = new Date(parseInt(datefield_from_server)); // string returned in remoted method response
datefield = new Date(datefield.getUTCFullYear(), datefield.getUTCMonth(), datefield.getUTCDate());
Javascript experts probably have a better way, but it works for me…
I’m still having a hard time seeing the advantage of remoting over the AJAX toolkit. Have you tried both or can you offer some input?
Sure. The AJAX toolkit is a wrapper on the webservices API and is based on the partner WSDL. Remoting let’s you call (specially annotated) Apex Controller methods from JavaScript in your Visualforce.
Thanks for the explanation. I guess what I’m getting at is in the example provided you’re just doing a DML operation which you could do just as easily with the AJAX toolkit without a controller action. Maybe I just need to dig up a more complex example to see the additional benefits.
Hi Wes,
I was wondering if you had ever seen this error before: Uncaught TypeError: Cannot read property ‘CLASS_NAME’ of undefined, VFRemote.js line 122. My javascript remoting is throwing this error.
Here’s what I’m confused about. This code works:
RemoteClassController.remoteMethod(param1,param2,param3,handleUpdatesCheck);
but this code doesn’t:
Visualforce.remoting.Manager.invokeAction(‘{!$RemoteAction.RemoteClassController.remoteMethod}’,param1,param2,param3,handleUpdatesCheck);
where handleUpdatesCheck is another function that handles the results. Do you have any idea why the first option works but the second doesn’t? I’d like your input because we’re using a managed package and I’d really like to avoid having to hack together a solution for managed/unmanaged if possible and I know the second option works both inside and outside of the package. Until then, I will use your workaround here: http://th3silverlining.com/2012/02/26/salesforce-javascript-remoting-and-managed-packages/
Thanks again!
–Liz