Last week I showed you how to make a simple custom lightning component and make it available to the new Lightning App Builder that is going GA with the Summer ’15 Release. This week, we will create a simple custom Poll Question object and a Poll Option object that will have the Poll object as a parent. We will then write the custom code to show the latest Poll inside of a Salesforce1 app.
Let’s start with creating the Poll Question object itself. We are going to keep it simple for now so there won’t be a ton of configuration wrapped around our app. Create a Poll Question object with the following fields (I added two more fields but haven’t used them yet in my code so I’ve left them out here as well). We will use the standard Name field as the question text so really we have one field here:
Status: Picklist with options 'Open' and 'Closed'
Next create a Poll Option object with the following fields (here we could probably use the standard Name field, but opted to have a custom field hold that information, in our case however they’d be identical, or we could make Name an auto number, etc):
Poll Question: Master/Detail to the Poll Question object
Option Name: Text to present to user for voting
Vote Count: Number, the current count of votes for this option
With that out of the way, we need to modify our component from last week to retrieve the latest poll and display it. Let’s first focus on getting the text of our question to show up in our component. We will need to create another aura:attribute in our component to hold our poll object and a place to show the question text on our component:
<aura:component implements="flexipage:availableForAllPageTypes" >
<aura:attribute name="pollQuestion" type="Poll_Question__c"/>
<aura:attribute name="title" type="String" />
<h1 style="text-align:center">{!v.title}</h1>
<p style="padding:10px;">{!v.pollQuestion.Name}</p>
</aura:component>
It occurs to me now that I may have forgotten to explain a few things about the attribute names and other markup you’re seeing here so allow me to explain. An aura:attribute has a name and a type. The type can be a custom object, a list of objects, or a primitive like a String. You can then refer to the data stored into these attributes by its name. In this case, we have an aura:attribute of type Poll_Question__c (our Poll Question object) and we refer to that object’s Name with the following markup: {!v.pollQuestion.Name}. In this case the “v” is an automatic variable that points to our visible component, or our “View” (think MVC — Model View Controller). Therefore {!v.pollQuestion.Name} basically says, in this component, find the attribute with the name of “pollQuestion” and get me the data stored there. Since that attribute contains a Poll Question, we can then get its Name for display as we would with any custom object.
So now that we have our component ready to display our Question, we need to tell the component how to get the latest question. We will do that with a “client-side” controller. This will be a bit of javascript that can call out to a custom apex controller that will lookup our poll object. First let’s create our javascript controller — the “client side” controller. It’s worthy to note that this controller is executed on the client, not on the server.
Ensure that you have the PollItem.cmp tab open in your developer console and click on the right-hand side’s menu item labeled “Controller.” This will create a new tab in the console labeled PollItemController.js. It will stub out the following code:
({
myAction : function(component, event, helper) {
}
})
We need to modify this code, but before we do that, we know that it will need some logic on the apex side for it to talk to. Rather than add a bunch of code here that won’t make much sense, let’s create our apex controller first. All this controller has to do for us right now is retrieve the latest Poll Question for us. For the apex side of things, you don’t need to use the developer console or any of the menu options on the right. (We will tell our visual component (PollItem.cmp) the name of this controller when we are ready.) So create an apex controller called, PollController using your favorite editor (the dev console is just fine as well). In that code we will have one method called “latestPoll()” and that is what our client side controller will call in order to get a handle on the latest poll. Here is that code:
public class PollController {
@AuraEnabled
public static Poll_Question__c latestPoll() {
Poll_Question__c poll = [SELECT Id
, Name
, Subject__c
, Total_Votes__c
, Highest_Votes__c
, (SELECT Id
, Vote_Count__c
, Option_Name__c
FROM Poll_Options__r)
FROM Poll_Question__c WHERE Status__c = 'Open'
ORDER BY CreatedDate LIMIT 1];
return poll;
}
}
This looks like standard apex code, because it is. Its just like any other controller you’ve worked with except for the @AuraEnabled annotation. Methods that are to be called from Lightning Components need to have this @AuraEnabled annotation.
With that out of the way we can revisit our client side controller and write the code that will call our apex:
({
doInit : function(component, event, helper) {
var action = component.get("c.latestPoll");
action.setCallback(this, function(response) {
var state = response.getState();
if(state === "SUCCESS") {
component.set("v.pollQuestion", response.getReturnValue());
}
});
$A.enqueueAction(action);
}
})
Okay — breathe, there’s quite a bit here for the un-initiated, I myself am still catching on, but we’ll get through this, I promise. This bit of javascript magic, when invoked, will look at our component (component.get) and look for our components controller (“c.latestPoll”) as declared (we haven’t added that yet, but we will in a moment). This is a bit of black magic that returns a handle to our apex controller function called “latestPoll()” — however it hasn’t run it yet. Its just setting up the call. The next bit “action.setCallback” is what is going to run AFTER our call to apex completes. Lastly the $A.enqueueAction(action) is what actually sends the call to apex to retrieve our data.
The component, event, and helper arguments to our doInit function are “auto wired” in some fashion that I don’t completely comprehend myself. Basically they are handles to our visual component that is calling this function, the event that is forcing the call to happen, and a helper function (if you create one). The latter is wired up via naming convention and is handled for you on the platform.
The setCallback runs after our call to apex completes and will then inspect the response we get back from our apex controller. If its successful, we will read our return value from the controller (in this case a Poll Question object) and pass that back to our pollQuestion aura:attribute on our visual component. (This javascript knows about the “v.pollQuestion” as well, just like our visual component knows about it {!v.pollQuestion}).
I expect that to be a little muddy right now and that’s okay. It will come to you, trust me. For now, pretend that I did my job and explained it flawlessly so we can move on to our last bit of code for this part. Let us wire up our visual component to our controllers. Here’s the code to do that, and then I’ll explain a few bits. Back in our PollItem.cmp add some code to look like this when you’re done:
<aura:component implements="flexipage:availableForAllPageTypes" controller="PollController" >
<aura:attribute name="pollQuestion" type="Poll_Question__c"/>
<aura:attribute name="options" type="Poll_Option__c[]"/>
<aura:attribute name="title" type="String" />
<aura:handler name="init" value="{!this}" action="{!c.doInit}" />
<h1 style="text-align:center">{!v.title}</h1>
<p style="padding:10px;">{!v.pollQuestion.Name}</p>
</aura:component>
First we’ve added to our component declaration: controller=”PollController” which is what our client side controller will look at for our @AuraEnabled methods to call. Secondly we’ve added something called an aura:handler. This is some code that allows us to hook into events that are occurring within our component. In this case we are hooking into a built in event called “init” which fires when this component is loaded. It has a value of {!this} meaning “this component” and when this event fires its going to look for a client side controller method called doInit ({!c.doInit}). Like the “v” in {!v.pollQuestion.Name} “c” is a built-in reference to a component’s client-side controller. (Again these are wired up via the framework for you…”black magic.” Its voodoo, I’m convinced).
So now when our component loads, it will fire the “init” event and since we have an aura:handler for the init event, it will call our client side controller’s doInit method. If you recall, that doInit method is what calls out to our apex controller which should return to us the latest Poll Question. If successful, it will then set the pollQuestion attribute of our component to the Poll Question object we just retrieved. (Savvy readers will notice I don’t have any error handling, I didn’t write any for this, but potentially you could handle errors gracefully here. Due to lack of time I haven’t gotten around to that yet…)
Let’s save this and use the standard UI to create a Poll Question and a couple Poll Options to display in our app. I created a Poll Question of “What would you like for lunch” and three options: Pizza, Sushi, and Burgers. Do that now, I’ll wait. (You may have to create tabs in your org for these objects, this tutorial assumes you know how to do all of that already).
When you are done, fire up Salesforce1 and go into your app. You should now see the Poll Question listed. Woohoo! Do you realize what you’ve done? You’ve wired up a Lightning Component to Salesforce data, pulled it back, and displayed it on a mobile device. Think about that for a second. Most of you are likely not mobile developers. Yet — here you are.
Next week, we will create a second visual component that we will iterate over in our PollItem component in order to display our options to the user. Until then consider working through the Lightning Module over at Trailhead. If I’ve managed to confuse you beyond belief, they oughtta be able to straighten you out :)
:wq!