public with sharing class ContactController { @AuraEnabled public static List<Contact> getContacts() { List<Contact> contacts = [SELECT Id, Name, MailingStreet, Phone, Email, LeadSource FROM Contact]; //Add isAccessible() check return contacts; } }
ContactController contains methods that return your contact data using SOQL statements. This Apex controller is wired up to your component in a later step. getContacts() returns all contacts with the selected fields.
<aura:component> <aura:attribute name="contact" type="Contact" /> <lightning:card variant="Narrow" title="{!v.contact.Name}" iconName="standard:contact"> <aura:set attribute="actions"> <lightning:button name="details" label="Details" onclick="{!c.goToRecord}" /> </aura:set> <aura:set attribute="footer"> <lightning:badge label="{!v.contact.Email}"/> </aura:set> <p class="slds-p-horizontal--small"> {!v.contact.Phone} </p> <p class="slds-p-horizontal--small"> {!v.contact.MailingStreet} </p> </lightning:card> </aura:component>
This component creates the template for your contact data using the lightning:card component, which simply creates a visual container around a group of information. This template gets rendered for every contact that you have, so you have multiple instances of a component in your view with different data. The onclick event handler on the lightning:button component calls the goToRecord client-side controller action when the buton is clicked. Notice the expression {!v.contact.Name}? v represents the view, which is the set of component attributes, and contact is the attribute of type Contact. Using this dot notation, you can access the fields in the contact object, like Name and Email, after you wire up the Apex controller to the component in the next step.
<aura:component implements="force:appHostable" controller="ContactController"> <!-- Handle component initialization in a client-side controller --> <aura:handler name="init" value="{!this}" action="{!c.doInit}"/> <!-- Dynamically load the list of contacts --> <aura:attribute name="contacts" type="Contact[]"/> <aura:attribute name="contactList" type="Contact[]"/> <aura:attribute name="totalContacts" type="Integer"/> <!-- Page header with a counter that displays total number of contacts --> <div class="slds-page-header slds-page-header--object-home"> <lightning:layout> <lightning:layoutItem> <lightning:icon iconName="standard:contact" /> </lightning:layoutItem> <lightning:layoutItem class="slds-m-left--small"> <p class="slds-text-title--caps slds-line-height--reset">Contacts</p> <h1 class="slds-page-header__title slds-p-right--x-small">Contact Viewer</h1> </lightning:layoutItem> </lightning:layout> <lightning:layout> <lightning:layoutItem> <p class="slds-text-body--small">{!v.totalContacts} Contacts • View Contacts Based on Lead Sources</p> </lightning:layoutItem> </lightning:layout> </div> <!-- Body with dropdown menu and list of contacts --> <lightning:layout> <lightning:layoutItem padding="horizontal-medium" > <!-- Create a dropdown menu with options --> <lightning:select aura:id="select" label="Lead Source" name="source" onchange="{!c.handleSelect}" class="slds-m-bottom--medium"> <option value="">-- Select a Lead Source --</option> <option value="Referral" text="Referral"/> <option value="Social Media" text="Social Media"/> <option value="All" text="All"/> </lightning:select> <!-- Iterate over the list of contacts and display them --> <aura:iteration var="contact" items="{!v.contacts}"> <!-- If you’re using a namespace, replace with myNamespace:contacts--> <c:contacts contact="{!contact}"/> </aura:iteration> </lightning:layoutItem> </lightning:layout> </aura:component>
Let’s dive into the code. We added the init handler to load the contact data during initialization. The handler calls the client-side controller code in the next step. We also added two attributes, contacts and totalContacts, which stores the list of contacts and a counter to display the total number of contacts respectively. Additionally, the contactList component is an attribute used to store the filtered list of contacts when an option is selected on the lead source dropdown menu. The lightning:layout components simply create grids to align your content in the view with Lightning Design System CSS classes.
The page header contains the {!v.totalContacts} expression to dynamically display the number of contacts based on the lead source you select. For example, if you select Referral and there are 30 contacts whose Lead Source fields are set to Referral, then the expression evaluates to 30.
Next, we create a dropdown menu with the lightning:select component. When you select an option in the dropdown menu, the onchange event handler calls your client-side controller to update the view with a subset of the contacts. You create the client-side logic in the next few steps.
In case you’re wondering, the force:appHostable interface enables your component to be surfaced in Lightning Experience and Salesforce1 as tabs, which we are getting into later.
({ doInit : function(component, event, helper) { // Retrieve contacts during component initialization helper.loadContacts(component); }, handleSelect : function(component, event, helper) { var contacts = component.get("v.contacts"); var contactList = component.get("v.contactList"); //Get the selected option: "Referral", "Social Media", or "All" var selected = event.getSource().get("v.value"); var filter = []; var k = 0; for (var i=0; i<contactList.length; i++){ var c = contactList[i]; if (selected != "All"){ if(c.LeadSource == selected) { filter[k] = c; k++; } } else { filter = contactList; } } //Set the filtered list of contacts based on the selected option component.set("v.contacts", filter); helper.updateTotal(component); } })
The client-side controller calls helper functions to do most of the heavy-lifting, which is a recommended pattern to promote code reuse. Helper functions also enable specialization of tasks, such as processing data and firing server-side actions, which is what we are covering next. Recall that the onchange event handler on the lightning:select component calls the handleSelect client-side controller action, which is triggered when you select an option in the dropdown menu. handleSelect checks the option value that’s passed in using event.getSource().get("v.value"). It creates a filtered list of contacts by checking that the lead source field on each contact matches the selected lead source. Finally, update the view and the total number of contacts based on the selected lead source.
({ loadContacts : function(cmp) { // Load all contact data var action = cmp.get("c.getContacts"); action.setCallback(this, function(response) { var state = response.getState(); if (state === "SUCCESS") { cmp.set("v.contacts", response.getReturnValue()); cmp.set("v.contactList", response.getReturnValue()); this.updateTotal(cmp); } // Display toast message to indicate load status var toastEvent = $A.get("e.force:showToast"); if (state === 'SUCCESS'){ toastEvent.setParams({ "title": "Success!", "message": " Your contacts have been loaded successfully." }); } else { toastEvent.setParams({ "title": "Error!", "message": " Something has gone wrong." }); } toastEvent.fire(); }); $A.enqueueAction(action); }, updateTotal: function(cmp) { var contacts = cmp.get("v.contacts"); cmp.set("v.totalContacts", contacts.length); } })
You must be wondering how your component works in Lightning Experience and Salesforce1. Let’s find out next!
For this tutorial, we recommend that you add the component as a custom tab in Lightning Experience.
When your component is loaded in Lightning Experience or Salesforce1, a toast message indicates that your contacts are loaded successfully. Select a lead source from the dropdown menu and watch your contact list and the number of contacts update in the view.
Next, wire up an event that navigates to a contact record when you click a button in the contact list.