Streams of Lightning: Part Four

Last week we updated our app to show all of the Poll Question Options as buttons within our parent Poll Question Component. However, it still “does nothing.” What we really need to do is get it to interact somehow, and we will do this by supplying our buttons with a function to call in order to cast our vote.

The first thing we need to do is setup a javascript controller (client side controller) for our Poll Question Item component to talk to. On it we will create a function called “voteItUp” that will in turn call a method on our apex controller that will actually increment the vote count and save our answer.

Open up your PollQuestionItem component in the developer console and in the right-hand side menu, click “Controller” and paste this into the code:

({   
    voteItUp : function(component, event, helper) {
        var optionId = component.get("v.pollOptionId");
        var voteAction = component.get("c.incrementVote");
        voteAction.setParams({ optionId : optionId });
        
        voteAction.setCallback(this, function(response) {
           var state = response.getState();
            if(state === "SUCCESS") {
                document.getElementById(optionId).innerHTML = response.getReturnValue();
            }
        });
        
        $A.enqueueAction(voteAction);
	}
})

What this does is gives us a function that we will use when our button is clicked. In order to determine which button is clicked we will store the option ID of the component that it came from (v.pollOPtionId) in a variable called “optionId.” If you recall we are looping over this component in our parent component and passing it a Poll Option object each time. We then setup the action (the call to the apex method) by looking at our component and finding its declared controller, and telling it to call the “incrementVote” method on that controller. Our apex controller needs to know what option we are voting on so we will have pass in the option ID. This is done with the setParams function and takes a key value pair collection of arguments (voteAction.setParams({ optionId : optionId }) ). We then tell this action what to do when it completes by setting up the callback function. This will run after our callout to apex completes. We’ll then inspect the results and if successful, update the UI.

Please Note: This code is still under some development as I was having some issues locating the output for my vote count. However, this was happening while simultaneously trying to troubleshoot some streaming API issues (coming in part 5). In order to get things to work, I had to resort to using document.getElementById to locate the element in my component to update it. However, that’s also the beauty of this — while its probably not the “blessed” way to do something, while coding you can always “fall back” to tricks like this because your UI, at the end of the day, is still simply HTML and javascript. I would like to come back at some point and fix this, but haven’t found the time :( #StoryOfMyLife

Next we will create the apex controller that our component’s client side controller will call. Simply create a controller using your favorite editor (dev console, sublime, eclipse etc). I called mine PollOptionController. Use the following code:

public class PollOptionController {
	    
    @AuraEnabled
    public static Integer incrementVote(String optionId) {
        Poll_Option__c option = [SELECT Id
                                , Vote_Count__c 
                                FROM Poll_Option__c 
                                WHERE ID = :optionId];
        option.Vote_Count__c += 1;
        update option;
        return option.Vote_Count__c.intValue();
    }
}

Here we take the optionId that we passed in from our client side controller and lookup our custom object by its Id. We then increment its vote count by 1, update it and return the vote count back to our calling function. This will get processed in the “callback” of our action and update our component’s UI with the latest vote count.

Lastly we need to tell our button what function to call when pressed and tell our component which apex controller it needs to be associated with like so:

<aura:component controller="PollOptionController">

    <aura:attribute name="pollOption" type="Poll_Option__c"/>
    <aura:attribute name="pollOptionId" type="String" default="{!v.pollOption.Id}"/>
    
    
    <div style="border:1px solid black;margin:5px;padding:10px;text-align:center;border-radius:5px">
        <ui:button aura:id="voteButton" label="{!v.pollOption.Option_Name__c}" press="{!c.voteItUp}"/>
        <div style="float:right" class="findme" id="{!v.pollOption.Id}">
            {!v.pollOption.Vote_Count__c}
        </div>
        <br/>
    </div>

</aura:component>

We tell our component which apex controller it will be working with by adding the controller attribute to our component declaration: controller=”PollOptionController” and finally we add the “press” attribute to our button to tell it which function to call when its tapped/clicked, etc. One point that may be confusing for some is the use of multiple controllers. Our UI components do not call our apex directly, instead they call our client side controllers (the javascript ones). That is the {!c.voteItUp} notation. However we do tell our component which apex controller it is associated with using the controller attribute in our declaration. So when I say our component is calling a controller function using c.something, I’m making reference to our client side controller, not our apex controller. (For me, it really feels as if the only reason our component mentions the apex controller is so that our client side controller has a place to look up which controller to call a given function on, I could be wrong and it may provide other functionality, but at the moment that bit is unclear to me).

If we load up our application in Salesforce 1, we should now be able to tap the buttons and vote. As you vote you will see the vote count increase. These results are also being persisted to your org as well. We now have a functional lightning app!

I was imagining a use for such an app, and perhaps rather than a “voting” type of application, similar apps could be used on shop floors, in retail etc to reflect an up to date view of inventory. But even better, what if we could stream those results so that users of the app could see an ongoing real-time view of data. For that, we will need some extra javascript libraries, some calls the the streaming API….and another blog entry. Until then…

:wq!