Voting online and seeing the results instantly on the web is a very convenient way to conduct quick surveys or pop polls. But you already know that because of the primary season. This real-time voting tutorial was made for the first technical workshop I led as an evangelist intern this summer, a at YouTube's headquarters. There, teaching to a group of women hackers of all skill levels (and ages), I found that the app appeals to both beginners and more advanced programmers because it was easily customizable and could be added to Facebook and Twitter using their APIs.
PubNub’s CTO, Stephen, and Ian, an engineer and evangelist, had a mini contest over who would have the most votes, proving that this project could make for fun conversation and competition. But you could also hold your own poll for this year's Presidential race. That's right, you can find out who would win if it were up to all your friends!
In this article, I am going to show you how to create your own voting app. Try the live demo in action!
Setup PubNub for a real-time voting app
First things first–get your PubNub publish and subscribe keys! Even though you are only publishing data, and not subscribing, you still need them both to initialize your PubNub object. You can get them here, where you register for an account, or by logging in to an existing account.
Next, you create a dictionary of polling options. The values are the counts of the number of votes, initialized at zero.
var pollOptions = { eon: { "Mushu" : 0, "Erlich" :0, "Stephen" : 0, "Tomomi" : 0, "Ian" : 0 } };
To set up the buttons in JavaScript, iterate through the keys in the dictionary, call document.createElement(‘BUTTON’) on each, set attributes like ID and style, and set the innerHTML to be the key of the dictionary. The listener for each button is a voteUp function, incrementing the counts.
function setupButtons() { for(key in pollOptions.eon) { var b = document.createElement('BUTTON'); b.setAttribute('id', 'button' + key); b.setAttribute('style', 'left:10%;width:6%;margin-left:4%;margin-top:4%;margin-bottom:5%;color:white;'); b.innerHTML = key; b.addEventListener("click", voteUp(key)); document.body.appendChild(b); } //for }
How to Update Vote Counts with a real-time PubNub voting system
VoteUp takes in a key, and increments that key’s value. It then publishes the dictionary to your channel. The incrementing and publishing must be wrapped in the following JavaScript closure in order to distinguish the incrementing of counts between the buttons. Once the value of a key-connected button is clicked, the value is incremented, and then published.
function voteUp(pollOptionKey) { return function() { pollOptions.eon[pollOptionKey] += 1; pubRes(); } //JS closure } function publishResults() { pb.publish({ channel: chan, message: pollOptions, callback: function(m) { console.log("publishing!"); } }); } //publishResults()
Create the Bar Graph with EON for real-time voting system
A bar graph best visualizes voting. To draw the graph in an HTML div titled chart, you just need to call the chart method on your eon object, and pass some parameters into it. Some are required, like your PubNub instance and your channel, but others are completely optional. Those ones, such as colors, bar width, labels, and more, are declared like so:
eon.chart({ pubnub: pb, channel: chan, history: true, generate: { bindto: '#chart', data: { labels: true, type: 'bar', colors: { 'Mushu': '#cc6699', 'Erlich': '#0099cc', 'Stephen': '#ffcc99', 'Tomomi': '#33cccc', 'Ian': '#0000ff' } }, bar: { width: { ratio: 0.5 } }, tooltip: { show: false } } }); }
Using PubNub's History API
Now, voting is most fun when lots of people do it! What if someone comes in from another browser after you? You need to keep track of previous votes with PubNub’s storage and playback, or History API. Even though you set history to be true above, you also need to go to your dashboard to enable the History API. Because each publish returns an associative array, or dictionary, of that total vote count, you only need to get the last message. The count of history is set to one, and the callback will take in that one, long message, and see that there is something there. It will then set your chart to be that long message, or associative array, of old votes.
There you have it! This is the bare minimum you need for a real-time voting application.
To take it further, you could use PubNub BLOCKS (still in beta, but launching soon) to restrict the number of times someone could vote. You could also set a timer so the chart only takes in new votes for a certain amount of time.
Adding social media widgets & more to your real-time voting system
To make the workshop more fun, I added some extra features with Twitter widgets and the Facebook API. Attendees learned about web intents, which lets visitors to your website tweet, reply, like, or follow from the context of that site. With no need to set up a Twitter application, store app credentials, or prompt the viewer for app permissions, this is a simple yet effective way to integrate social media into a web project. The tweet intent let them create a pre-composed tweet that could be tweeted from a widget on their site.
<a href="https://twitter.com/intent/tweet?text=learning+how+to+make+a+realtime+web+voting+app+with+@pubnub&hashtags=sospectra&via=lizziepika">Tweet</a>
Each parameter is optional, but the text, hashtags, and via ones are the most fun to use. Text sets the tweet, and via lets you include another twitter user. Twitter offers many other ways to customize widgets and pre-compose tweets, like images and icons.
Both Twitter for Developers and the Facebook API needed to make sure that the user was logged in, but Facebook’s was more complex. Everyone needed their own API keys, and the API features needed to run on a server– it was not enough to just run the HTML page. It needed to check that you were logged in to Facebook before it could post a previously-composed status automatically for you, and then made a call to the Facebook API.
One huge difference between the two is that clicking the Facebook button does not ask the user for any confirmation; it merely posts on your behalf, whereas with the Twitter web intent widget, the user has the opportunity to edit or delete the tweet before posting.
To integrate the Facebook API (after logging into the Facebook Dev site and creating an app), first you must initialize the SDK. This object takes in parameters that are fairly self-explanatory, like your personal app id and the API version.
XFBML is one that is not so clean-cut. XFBML are XML elements which display social plugins for HTML pages, determining whether XFBML tags from social plugins are parsable, and, consequently, if they are rendered.
window.fbAsyncInit = function() { FB.init({ appId : 'your-facebook-app-id', xfbml : true, version : 'v2.7' }); };
Next, you need a function to load the Facebook SDK.
(function(d, s, id){ var js, fjs = d.getElementsByTagName(s)[0]; if (d.getElementById(id)) { return; } js = d.createElement(s); js.id = id; js.src = "//connect.facebook.net/en_US/sdk.js"; fjs.parentNode.insertBefore(js, fjs); }(document, 'script', 'facebook-jssdk'));
Then, a Facebook dialog prompts the user to login if they have not already done so. The function to login can handle more parameters for further ways to authenticate the user, in addition to just logging in. In this case, however, there are none.
function myFacebookLogin() { FB.login(function(){ FB.api('/me/feed', 'post', {message: 'Hello, world! Having fun building a real-time voting app with #PubNub and #Twitter and #Facebook APIs #sospectra'}); }, {scope: 'publish_actions'}); }
After that, you call the Facebook graph API to request data from the site. Facebook has APIs for topics like marketing, the public feed for user and page status updates, and the key insights API. In this tutorial, all you need is the graph API, named after the concept that the social media site is a “social graph” containing nodes (pictures, pages, comments, users), edges (connections between the nodes), and fields (information about nodes and edges, like birthdays).
The graph API call for this tutorial takes three parameters: the route or path you wish to call to (here, that would be your social feed), the HTTP method you need from the API (to post, in this case), and the message the method will do.
function myFacebookLogin() { FB.login(function(){ FB.api('/me/feed', 'post', {message: 'Hello, world! Having fun building a real-time voting app with #PubNub and #Twitter and #Facebook APIs #sospectra'}); }, {scope: 'publish_actions'}); }
The Facebook developers' documentation was much harder to follow than I thought it would be. Though I am not the only one who needed to do quite a bit of digging in other resources to understand and use this API, I hope this tutorial helps you and that the developer resources for it do not deter or hinder you from using it.
Conclusion: Using PubNub to build a real-time voting app
The full code for the workshop can be viewed in the GitHub repo, and the slides are also on SlideShare. I was surprised at how much time I needed to put in to prepare for the workshop, and I wish I had tried to predict possible questions beforehand. Other than that, I found it enjoyable and rewarding, and hope to lead it again at future hackathons.
If you make your own real-time voting app, or have any thoughts or questions, find me on Twitter!