Saturday, September 29, 2018

Lightning Components Basics : Considerations while calling server-side controller actions

In this blog, we will cover how to call apex class method from Lightning component and different considerations that we need to consider while performing server side call. Actually all calls to server for apex class routed through controller.js function. From lightning component, you can call controller.js function from where you can call apex method.

First of all in order to associate an apex class with lightning component, you have to specify apex class name in controller attribute.

Also remember that all apex methods or apex member variables needs to annotated with @AuraEnabled.

Below is sample apex class which will be called from lightning:

public class SK_LightningBasicsSSController {
    @AuraEnabled
    public static List<Account> findRecentAccounts(){
        return [select id,name,type from Account 
                order by LastmodifiedDate DESC Limit 10];
    }
}

In order to associated this class with lightning component, use below syntax:

<aura:component controller="SK_LightningBasicsSSController">
    <!-- you code-->
</aura:component>

Now we will call apex method on load of lightning component and will display returned list of accounts.

Below is process to call apex method from controller.js

//Create instance of apex method by using c. annotation followed by method name.
var actionName= component.get("c.findRecentAccounts");
//Specify the callback function as all calls are asynchronous. this callback will be invoked once //response is returned by apex class
actionName.setCallback(this, function(response) {
var state = response.getState();
 //Check response state for SUCCESS and ERROR
if (state === "SUCCESS") {
  //store the response in variable
var apexResponse=response.getReturnValue();
console.log('***'+JSON.stringify(apexResponse));
                //set response to attribute value
component.set("v.ltngAccountList",apexResponse);
}else if(state === "ERROR"){
var errors = response.getError();
console.error(errors);
alert('Problem with connection. Contact your system administrator.');
}
});
//do not forget to add below line as this put this request in queue for asynchronous call
$A.enqueueAction(actionName);

Below is complete code snippet:



Important Points to remember:

How to call apex method which contains parameters


If you have method defined with some parameter, for example search string to search account as mentioned below:

@AuraEnabled
Public static List<Account> SearchAccounts(string searchString){
       //your logic
}

So in order to pass parameters to apex class method, create a JSON as mentioned below:
Suppose you have an attribute defined to take input from user say AccName:

<aura:attribute name="AccName" type="string"/>

var actionName= component.get("c.SearchAccounts");
var params = {
                         "searchString" :component.get("v.AccName")
                      };
actionName.setParams(params);

Note:
  • While creating JSON for parameters, key Value should be similar to parameter name in apex method. In our case the apex method parameter name was "searchString" so while creating JSON we specified first key Value as "searchString".
  • If you have multiple parameters, then create JSON as mention below:
          var params ={
                                 "param1":"param value1",
                                 "param2": "param value2",
                                 "param3": "param value3"
                               };

Create helper function which can be reused for different apex method calls

Instead of writing same piece of code to call different apex methods, we can write function in helper.js which can be invoked from controller.js function by passing the parameters to it.

I have created a helper function which takes 4 parameters:
  • Component reference
  • apex class method name
  • callback function name
  • param (to specify parameters if apex method expect some parameters or arguments)
Below is helper function "callToServer"

({
callToServer : function(component, method, callback, params) {
        var action = component.get(method);
        if(params){
            action.setParams(params);
        }
        console.log('****param to controller:'+JSON.stringify(params));
        action.setCallback(this, function(response) {
            var state = response.getState();
            if (state === "SUCCESS") {
                callback.call(this,response.getReturnValue());
            }else if(state === "ERROR"){
                var errors = response.getError();
                console.error(errors);
                alert('Problem with connection.'+errors);
            }
        });
        $A.enqueueAction(action);
    }
})

Now if you have to call apex class method from controller.js then use below syntax:

({
    doInit : function(component, event, helper) {
        var params = {
                                 "searchString" :component.get("v.AccName")
                              };
        helper.callToServer(
            component,
            "c.SearchAccounts",
            function(response)
            {
                console.log('apex response :'+JSON.stringify(response));
                component.set("v.ltngAccountList",response);
            }, 
            params
        );
    }
})

How to call different apex methods in sequential order

All calls to apex methods are asynchronous so if there is need to first call a apex method and based on response call another apex method, then you need to make sure that you perform second call to server after getting the response from first function.
So always perform second call to server from inside the callback function which get invoked when asynchronous call finished on server. 

If you use helper function approach to call apex methods then you can use below syntax to call 2 different (searchAccounts and findAccountdetailsapex method in sequential order.

var param1= {
                         "searchString" :component.get("v.AccName")
                     };
helper.callToServer(
component,
"c.SearchAccounts",
function(response)
{
component.set("v.ltngAccountList",response);
var selectedId = response[0].id;
var param2 = {
"ltngAccId": selectedId
}
//creating component dynamically using helper function
helper.callToServer(
component,
"c.findAccountdetails",
function(response)
{
   //your logic
},
param2
);
}, 
param1
);

Use Wrapper to get all data in single call

Avoid multiple calls to server in order to fetch information as it will degrade the performance.
Use wrapper (user defined data types) to return complete information in single server call.


Hope this will help!!!


1 comment: