Salesforce: Make Ajax calls with Lightning Components

LockerService has been a source of frustration for many Lightning component developers. But the fact remains that LockerService is here to stay. It’s an important part of security in when using Lightning components from different sources.

LockerService is not just bad news. Now that it’s mandatory, developers can use many long-awaited features. One of those is making Ajax calls directly from Lightning components. This blog will walk you through a demo of making an AJAX call to Google APIs to get information about a location.

Let’s start by going to SetupCSP Trusted Sites and adding https://maps.googleapis.com as a trusted site. This will allow a Lightning component to make a call to this domain and get information back.

First I decided to build a utility component that can be called from any component to make an Ajax call and process result. Feel free to use this component for demo or actual use as needed.

Utils.cmp

<aura:component >
    <!-- Define method to be called by other components -->
    <aura:method name="callAjax" action="{!c.callAjax}">
        <!-- Method can be, for example, GET or POST -->
        <aura:attribute name="method" type="String" default="GET" />
        <!-- URL to call -->
        <aura:attribute name="url" type="String" />
        <!-- Whether the call should be sync or async -->
        <aura:attribute name="async" type="Boolean" default="true" />
        <!-- Callback method on call complete -->
        <aura:attribute name="callbackMethod" type="Object" />
    </aura:method>
</aura:component>

UtilsController.js

({
    callAjax : function(component, event, helper) {
        //Creaet new request
        var xmlhttp = new XMLHttpRequest();
        //Handle response when complete
        xmlhttp.onreadystatechange = function(component) {
            if (xmlhttp.readyState == 4 ) {
                console.log('xmlhttp: ' + xmlhttp);
                params.callbackMethod.call(this, xmlhttp);
            }
        };

        var params = event.getParam('arguments');
        if (params) {
            console.log('params:', params);
            //Set parameters for the request
            xmlhttp.open(params.method, params.url, params.async);
            //Send the request
            xmlhttp.send();
        }
    }
})

XMLHttpRequest.cmp

<aura:component >
    <!-- Address to send Google to get more information -->
    <aura:attribute name="address" type="String" access="global" default="1 Market St, San Francisco, CA 94105, USA" />

    <!-- Google API key to send if needed; OPTIONAL -->
    <aura:attribute name="apikey" type="String" access="global" />


    <!-- Message information for ui:message component -->
    <aura:attribute name="msg" type="String" default=""/>
    <aura:attribute name="msgSeverity" type="String" />
    <aura:attribute name="msgTitle" type="String" />

    <!-- Add utils component to use aura:method -->
    <c:Utils aura:id="utils" />

    <div class="slds">
        <!-- User input to ask for address and API key if needed -->
        <lightning:input type="text" label="Address" name="{!v.address}" value="{!v.address}" />
        <lightning:input type="text" label="API Key" name="{!v.apikey}" value="{!v.apikey}"/>

        <!-- Send request to Google on button click -->
        <lightning:button label="Call Ajax" onclick="{!c.buttonPress}" />

        <!-- Display errors or return text on success -->
        <aura:if isTrue="{! v.msg != '' }">
            <ui:message severity="{!msgSeverity}" title="{!v.msgTitle}">
                {!v.msg}
            </ui:message>
        </aura:if>
    </div>
</aura:component>

XMLHttpRequestController.js

({
    buttonPress : function(component, event, helper) {
        //Generate URL for request to Google APIs
        var url = 'https://maps.googleapis.com/maps/api/geocode/json?address=' + component.get('v.address');

        //Add API key if provided
        if(!$A.util.isUndefined(component.get('v.apikey'))){
            url += '&key=' + component.get('v.apikey');
        }

        //Make Ajax request
        helper.makeAjaxRequest(component, url);
    }
})

XMLHttpRequestHelper.js

({
    makeAjaxRequest : function(component, url) {
        var utils = component.find('utils');
        
        //Make Ajax request by calling method from utils component
        utils.callAjax("POST", url, true,
                        function(xmlhttp){
                            console.log('xmlhttp:', xmlhttp);

                            //Show response text if successful
                            //Display error message otherwise
                            if (xmlhttp.status == 200) {
                                component.set('v.msg', xmlhttp.responseText);
                                component.set('v.msgSeverity', 'information');
                                component.set('v.msgTitle', 'Success');
                            }
                            else if (xmlhttp.status == 400) {
                                component.set('v.msg', 'There was an error 400');
                                component.set('v.msgSeverity', 'error');
                                component.set('v.msgTitle', 'Error');
                            }else {
                                component.set('v.msg', 'Something else other than 200 was returned');
                                component.set('v.msgSeverity', 'error');
                                component.set('v.msgTitle', 'Error');
                            }
                        }
                      );
    }
})

On success, you should see return response from maps.googleapis.com for provided address.

This is a very welcome change and will solve many issues now that we can make calls directly from Lightning components. I came across it this week and wanted to test and share it here.

Note: Source code used in this blog is available at https://github.com/jrattanpal/Blog-LC-Ajax

2 thoughts on “Salesforce: Make Ajax calls with Lightning Components

Leave a Reply