Accueil > Conseils > Lightning Components & AuraEnabled method parameters: What’s working and what’s not

Lightning Components & AuraEnabled method parameters: What’s working and what’s not

 7 minutes de lecture

AUTEUR
DATE

avril 10, 2017

CATEGORIES
HASHTAGS
PARTAGEZ !
ABONNEZ-VOUS :

Passing values from client-side to server-side is an important part of Lightning Component Development, and is pretty easy once you understood the way to write it. In theory.

Passing a value to a server-side controller

Let’s take a very simple example of a server-side call in a Lightning Component. There is 3 parts in this call:

  • An attribute value stored on the component markup
  • A server-side Apex controller that will receive and use the value
  • A client-side JavaScript controller that will make the link between them

 

A very basic component that would send a String to the server would look like this:

Component

<aura:component controller="myApexController">
<aura:handler name="init" value="{!this}" action="{!c.callServer}"/>
<aura:attribute name=”myAttribute” type=”String” default=”Hello World”/>
</aura:component>

JavaScript Client-side Controller

callServer : function(component, event, helper) {
var myAttribute = component.get("v.myAttribute");
  var action = component.get("c.setAttribute");
action.setParams({ "myString" : myAttribute });
action.setCallback(this, function(response) {

var state = response.getState();
if (state === “SUCCESS”) {
// Do stuff
}
else {
console.log(state);
}
});
  $A.enqueueAction(action);
}

Apex Server-side Controller

@AuraEnabled
public static void setAttribute(String myString) {
System.debug(myString);
}

As you would expect, the client-side attribute and the apex method parameter have the same type. On Lightning side, available aura:attribute types are listed here. On server-side, well it’s Apex, so you can make use of the types you’re used to.

However, using the same type client-side and server-side doesn’t always work the way you would expect. If using a String, Boolean or DateTime (non-exhaustive list) is working fine, some other types have some glitches that may drive you crazy the first time you encounter them. Let’s have a look.

Integer

Integer was the first type I encountered issues with. This is such a basic type that it took me some time to understand that it wasn’t my code that had an issue, but the framework itself. Let’s take the following example:

Component

<aura:attribute name=”myInteger” type=”Integer” default=”1”/>

Note: I won’t write again the JavaScript Client-side Controller, as it’s only used to pass the value from the Component markup to the Apex server method, and would be exactly the same for all our examples.

Server-side Controller

@AuraEnabled
public static void setInteger(Integer myInteger) {
System.debug(myInteger);
}

You can run this code, have a look to the logs, and feel happy: your integer value will be displayed. Until you use it. Just add the following line to your Apex method:

myInteger++;

If you run your code again, you’ll see that it’s crashing with an error server-side: FATAL_ERROR Internal Salesforce.com Error. This is because the myInteger parameter isn’t casted correctly to an Integer by the framework. So you’ll need to simply cast it again manually in your Apex code, and then everything will be fine:

myInteger = Integer.valueOf(myInteger);
myInteger++;

This is a very easy workaround that can save you time if you encounter a strange behavior while using an Integer attribute. This is not only about doing maths on your attribute, but any other operation that needs an Integer and not a String. Another good example is using it inside an soql query, for a Limit for instance.

sObjects

These are really important in Lightning, as you’ll have to manipulate Salesforce objects all the time. Don’t worry, they are working fine, and you can safely pass one sObject attribute from client-side to server-side.

The only thing I’ve noticed, is that relationships won’t be available in the Apex method attribute: For instance, if you have a Account attribute, with a list of Contacts populated in the sObject, once in the Apex method

On a side note, remember that with Lightning Data Service you may not even have to query the server anymore to get the record you need.

Date

I was not able to use Date in an AuraEnabled method, the passed attribute always ends up being null:

Component

<aura:attribute name=”myDate” type=”Date” default=”1981–08–26"/>

Server-side Controller

@AuraEnabled
public static void setDate(Date myDate) {
System.debug(myDate); // --> null
}

However, the workaround is pretty easy to implement, just pass the date as a string, and then cast it to a Date:

@AuraEnabled
public static void setDate(String myDate) {
Date myNewDate = Date.valueOf(myDate);
}

Again, this is easy to do, but you can lose some time just asking yourself what is wrong in your code and why the date is null in your Apex controller.

Apex Class

You can use an Apex class as an aura:attribute type. This can be convenient if you want to pass your data in a specific format while having a more structured way than just writing a json object manually. For instance, you’ll need it if you want to use SelectOption at some point.

Unfortunately, I was not able to make it work. Every time I tried I received the following error: An internal server error has occurred.

Apex NewWrapperClass

public class NewWrapperClass {
@AuraEnabled
public String label {get; set;}
}

Component

<aura:attribute name=”myApexClass” type=”NewWrapperClass” />

Client-side Controller

callServer : function(component, event, helper) {
var myClass = component.get("v.myApexClass");
  var action = component.get("c.setApexClassType");
action.setParams({ "myApexClass" : myClass });
action.setCallback(this, function(response) {
// Do stuff
});
  $A.enqueueAction(action);
}

Server-side Controller

@AuraEnabled
public Static void setApexClassType(NewWrapperClass myApexClass) {
System.debug(myApexClass);
}

The best way I found to pass an Apex class to your AuraEnabled method is stringify it on client-side, and then deserialize it in Apex. The following code will work fine:

Client-side Controller

callServer : function(component, event, helper) {
var myClass = component.get("v.myApexClass");
  var action = component.get("c.setApexClassType");
action.setParams({ "myApexClass" : JSON.stringify(myClass) });
action.setCallback(this, function(response) {

var state = response.getState();
if (state === “SUCCESS”) {
// Do stuff
}
else {
console.log(state);
}
});
$A.enqueueAction(action);
}action.setParams({ wrap :

Server-side Controller

@AuraEnabled
public static void setApexClassType(String myApexClass) {
NewWrapperClass nwc = (NewWrapperClass)JSON.deserialize(myApexClass, NewWrapperClass.class);
}

Set

You won’t be able to use a Set, but at least you won’t have any surprise with this one. Try to save an AuraEnabled method with a parameter of type Set<String>, and you’ll see the following error message, preventing you to save: Parameter type does not support AuraEnabled: Set<String>.

List and Map are working perfectly though.

Bonus tip: base64

This is something that is not specific to Lightning, but that may be useful if you want to pass a file to your Apex method. Don’t forget to encode/decode the file:

Client-side Controller

var action = component.get("c.setFile");
action.setParams({ myBase64File : encodeURIComponent(base64File) });
action.setCallback(this, function(response) {

var state = response.getState();
if (state === “SUCCESS”) {
// Do stuff
}
else {
console.log(state);
}
});
$A.enqueueAction(action);

Server-side Controller

@AuraEnabled
public static void setFile(String myBase64File) {
String myFile = EncodingUtil.urlDecode(myBase64File, ‘UTF-8’);
}

Wrapping up

Lightning is still a young framework, and I’m sure most of the strange behaviors described here will be fixed in the future. However in the meantime, I hope this will help some of you save the time I’ve lost when I encountered them.

Also, if you’ve seen some other weird things I would have missed with other types, please feel free to add them in the comments.