Tuesday, July 17, 2018

Salesforce Lightning Tutorial - Part 1 | Fetch and display data from server



This is the first post in the Lightning Tutorial Series. In this post, you'll learn how you can you fetch data from an apex controller and display it in the lightning component. Mainly the read operation in CRUD (Create, Read, Update and Delete). If you want to learn about the very basics of lightning like:- what is lightning ? What are the pre requisites ? etc. then you can refer to my previous post Salesforce Lightning Basics.

Now, we are going to code a lightning component that will display all the contacts related to an account and then we'll replace it with the standard related list of contacts under the account detail page in lightning.

1. Create an Apex Controller

// Apex Controller for Contact List Lightning Component
public class ContactListController {
        
    @AuraEnabled
    public static List<Contact> getContactList(List<Id> accountIds) {
        // Getting the list of contacts from where Id is in accountIds
                List<Contact> contactList = [SELECT Id, Name, Email, Phone, AccountId FROM Contact WHERE AccountId in :accountIds];
                // Returning the contact list
        return contactList;
    }
}
As you can see in the above code that I have created a very simple apex controller which takes a single argument i.e. a list of account ids and return a list of contacts related to all the accounts whose ids are passed as a parameter. The above method is static as the non- static @AuraEnabled methods cannot have parameters. The @AuraEnabled annotation is used to specify that this method will be called from lightning component.

2. Create a Lightning Component

<aura:component implements="flexipage:availableForRecordHome,force:hasRecordId" controller="ContactListController" access="global" >
    <!-- Handler to call function when page is loaded initially -->
    <aura:handler name="init" action="{!c.getContactsList}" value="{!this}" />
    <!-- List of contacts stored in attribute -->
    <aura:attribute name="contactList" type="List" />
    <!-- Lightning card to show contacts -->
        <lightning:card title="Contacts">
        <!-- Body of lightning card starts here -->
        <p class="slds-p-horizontal_small">
            <!-- Aura iteration to iterate list, similar to apex:repeat -->
            <aura:iteration items="{!v.contactList}" var="contact">
                <!-- recordViewForm to view the record -->
                <lightning:recordViewForm recordId="{!contact.Id}" objectApiName="Contact">
                    <div class="slds-box slds-theme_default">
                        <!-- outputfield used to output the record field data inside recordViewForm -->
                        <lightning:outputField fieldName="Name" />
                        <lightning:outputField fieldName="Email" />
                        <lightning:outputField fieldName="Phone" />
                    </div>
                </lightning:recordViewForm>
                <!-- Line break between two records -->
                <br />
            </aura:iteration>
        </p>
        <!-- Lightning card actions -->
        <aura:set attribute="actions">
            <!-- New button added -->
            <lightning:button label="New" onclick="{!c.newContact}" />
        </aura:set>
    </lightning:card>
</aura:component>
If you remember, I have told in my previous post that Lightning Component tags are like html tags. In the above code, the lightning component implements flexipage:availableForRecordHome ( as we have to embed our component in a standard record detail page ) and force:hasRecordId ( this will help the component to get the record Id of the record which is being displayed on the page, here it will be the account id as we are going to embed this component in account detail page ). After this, I have used a aura:handler tag with attribute name - init and value - {!this} this name and value is fixed as we are going to specify a function in action which will be called automatically when the record is loaded initially. In the action, I have written c.getContactsList here c stands for controller but it's not the apex controller, it's the lightning controller in which I have defined the getContactsList function which I am calling on initial loading of the page.

So, till now we have made a lightning component in which getContactsList method is called which will mainly be used to fetch the list of contacts from apex controller we previously created. But wait...where are we going to store this list ? Correct..!! We need an apex attribute to store the list. So, I have used an apex attribute tag which is of type list as I have to store list in this attribute ( remember..we returned a list from apex controller method).

Next steps are pretty simple and straight forward:-

  1. I used a lightning card tag to show my contacts wrapped in a card and gave the title Contacts
  2. Lightning Card body consists of a paragraph tag with an slds class so defined the same. You can have a detailed look at lightning card tag here.
  3. In the body I have to display the contacts list, so I have used aura iteration tag and in the items, I have given {!v.contactList} as I have to iterate through the contactList to show the contacts and each contact is represented using a var which is given the value contact. It is very similar to apex:repeat if you have worked on visualforce pages before.
  4. After that, I have used lightning:recordViewForm tag which is mainly used to display a record. You just have to pass the recordId and the objectApiName as the attributes which I have given as {!contact.Id} and Contact respectively.
  5. In the lightning recordViewForm, I have used lightning:outputField tag in which we just have to pass the field name and I have done so. You can have a detailed look at lightning recordViewForm tag here.
  6. Before closing the lightning card tag, I have used aura:set tag in which I have given the attribute as actions as I am going to add action buttons in this.
  7. Finally, I have added a lightning button with label New and given a controller function in the onclick attribute which will be called when the button is clicked.

3. Creating a Lightning Controller

({
    // Function called on initial page loading to get contact list from server
        getContactsList : function(component, event, helper) {
        // Helper function - fetchContacts called for interaction with server
                helper.fetchContacts(component, event, helper);
        },

    // Function used to create a new Contact
    newContact: function(component, event, helper) {
        // Global event force:createRecord is used
        var createContact = $A.get("e.force:createRecord");
        // Parameters like apiName and defaultValues are set
        createContact.setParams({
            "entityApiName": "Contact",
            "defaultFieldValues": {
                "AccountId": component.get("v.recordId")
            }
        });
        // Event fired and new contact dialog open
        createContact.fire();
    }
})

This is very simple. In lightning controller, we mainly keep the functions that are called from the page. It consists of 3 parameters component, event and helper which are used to interact with the component, event and helper respectively. If you want to pass another parameter, you can add after these. I personally prefer to keep all the client side actions in the controller itself and use helper for the server calls. In the above code, I have created two functions, getContactsList and newContact. If you remember I called the getContactsList from the init handler to fetch the data, so this require a server call. Therefore, I simply called the fetchContacts function from the helper which will be doing everything so we'll discuss it later. For now, let's concentrate on newContact function which will fire an event to create a new contact. In this function, $A is used to reference global event e.force:createRecord. We assigned this event to createContact variable. Then we set the parameters entityApiName as Contact and in the defaultFieldValues I have specified the AccountId only as I want this to be filled by default and I have given recordId in this which is the id of account under which we are creating a contact. Then, we fire the event. Remember the create record dialog which we get when we create a new record...?? This will do exactly the same thing. The global event can run only in one.app container. In other words, new button will work only when our component is embedded somewhere in a detail page ( having one.app - salesforce org url ).

4. Creating a Lightning Helper

({
    // Function to fetch data from server called in initial loading of page
        fetchContacts : function(component, event, helper) {
        // Assign server method to action variable
        var action = component.get("c.getContactList");
        // Getting the account id from page
        var accountId = component.get("v.recordId");
        // Setting parameters for server method
        action.setParams({
            accountIds: accountId
        });
        // Callback function to get the response
        action.setCallback(this, function(response) {
            // Getting the response state
            var state = response.getState();
            // Check if response state is success
            if(state === 'SUCCESS') {
                // Getting the list of contacts from response and storing in js variable
                var contactList = response.getReturnValue();
                // Set the list attribute in component with the value returned by function
                component.set("v.contactList",contactList);
            }
            else {
                // Show an alert if the state is incomplete or error
                alert('Error in getting data');
            }
        });
        // Adding the action variable to the global action queue
        $A.enqueueAction(action);
        }    })
This is the last part of our lightning component in which, we'll hit the apex controller method, pass the account id, fetch all the contacts related to the account and finally push all the contacts in the contactList attribute. A helper method also consists of 3 parameters component, event and helper which are passed from the controller from where it is called. It is not compulsory to pass all 3 but recommended to do so and use a 4th parameter to pass anything else. I have used component.get()function in the first line that takes the apex controller method as a parameter and assigned it to actionvariable. So, I have passed c.getContactList in it where refers to apex controller and getContactList is the method we defined in our apex controller. I have used the same component.get() to get the account id from the page. The only difference is that while referencing the page I have used v instead of c as - v.recordId. I have set the parameters for action which is the parameters my apex controller method is using. In the accountIds of apex controller I have passed accountId which is my javascript variable. action.setCallback will hit the server method with parameters and we have a response in return. The response.getState() is used to check whether our call is successful or not and response.getReturnValue() gives us the list of contacts which were returned from the apex controller method. Finally, I have set the list fetched from apex controller method to my own aura attribute in component which is of type List and then I have added this action to the global action queue by using $A.enqueueAction() and passing the action as parameter in this function. All this functionality is performed during the initial loading of the page.

Congrats..!! you just made your first lightning component. It looks like this:-

And when you click on the new button, you'll have the standard dialog which is as follows:-

No comments:

Post a Comment