Advertisement
Mobile Web Apps

Build a jQuery Mobile Survey App: App Logic & Interface

by

Welcome to part II of the tutorial series on creating a quiz or survey web application with jQuery Mobile and Ruby on Rails. In this part of the tutorial we are going to create a mobile friendly web interface with jQuery Mobile so that our survey can be taken easily on any HTML5 compatible smartphone. This can be done very easily as jQuery Mobile comes with pre-defined CSS templates that look great in mobile browsers in addition to being a great javascript library that helps developers create "app-like" experiences for mobile websites.

Before we start, I have included a bonus rake task inside the sample code for this part of the tutorial that generates some sample questions. To run this, simply execute this in the command line inside the directory of the Rails app:

rake setup:survey

The source for this task is located at lib/tasks/setup.rake

We'll start by generating a show action for our questions controller in app/controllers/questions_controller.rb.

def show
    @question = Question.find(params[:id])
    @choices = @question.choices
end

Our show action here is very simple. We are loading a question from the database by its ID. We are storing the choices for the question in an instance variable for later access in our view. You'll notice that since we setup a has_many relationship between the questions and choices, we "automagically" get the convenience method of being able to retrieve all the choices for a question by calling "@question.choices." By default, Rails will load our view from the show.html.erb file which we will create later.

Next, lets create the "answer" action inside our questions controller which will take a user's response to a question and store it in the database.

 def answer
    @choice = Choice.find(:first, :conditions => { :id => params[:id] })
    @answer = Answer.create(:question_id => @choice.question_id, :choice_id => @choice.id)
    
    if Question.last == @choice.question
      render :action => "thankyou"
    else
      question = Question.find(:first, :conditions => { :position => (@choice.question.position + 1) })
      redirect_to question_path(:id => question.id)
    end
 end

As we explained before when we created the table to store answers, an answer is simply the combination of a question's ID and a choice ID. Since we don't have the concept of a user in this system, we are simply going to store answers and look at the results en masse later on. Let's break it down:

    @choice = Choice.find(:first, :conditions => { :id => params[:id] })
    @answer = Answer.create(:question_id => @choice.question_id, :choice_id => @choice.id)

In the above code, we are finding the choice in the database by its id. We then are creating an answer object that is comprised of the question_id and the ID we get from the choice object.

    if Question.last == @choice.question
      render :action => "thankyou"
    else
      question = Question.find(:first, :conditions => { :position => (q.position + 1) })
      redirect_to question_path(:id => question.id)
    end

After a user answers a question, we have a decision that will determine what to show to the user. If the user has answered the last question we have stored in the database (which we can retrieve with "Question.last"), we are going to render our "thank you for completing the survey" view. If it's not the last question, we are going to find the question with a "position" of the current question's position plus 1. Then we will redirect to the show action for that question with the RESTful rails helper method of question_path. For more info on creating RESTful rails controllers, do a google search for "RESTful Rails 3" and read some of the articles that people have posted.

In the RESTful world, there is no such thing as an answer action, so we have to add this to our config/routes.rb file.

Simply replace this line:

resources :questions

with this:

resources :questions do
	collection do
	  get :answer
	end
end

Currently, if a user hits the root URL of our server, they will be given an error. To prevent this we are going to add this root option to our config/routes.rb file as well:

root :to => "questions#index"

This line will direct the request of the root URL to the index action of the question's controller. While we haven't defined an actual index action, by default Rails will load the index.html.erb file as the view. We are going to create this file later on.

Now that our Rails work is essentially done, we are going to start creating the views that will make use of the jQuery Mobile framework. We'll start by creating a global layout for our template at app/views/layouts/application.html.erb.

<!DOCTYPE html>
<html>
<head>
  <title>Survey</title>
  <link rel="stylesheet" href="http://code.jquery.com/mobile/1.0a1/jquery.mobile-1.0a1.min.css"/>
  <script src="/javascripts/jquery.min.js"></script>
  <script src="/javascripts/application.js"></script>
  <script src="http://code.jquery.com/mobile/1.0a1/jquery.mobile-1.0a1.min.js"></script>
  <%= csrf_meta_tag %>
</head>
<body>
  <div data-theme="b" class="ui-page-active" data-role="page">
    <%= yield %>
  </div>  
</body>
</html>

In the head section, you'll notice that we are loading 2 jQuery Mobile specific files from the jQuery site: 1 CSS file and 1 JS file. This is fine for development mode, but if we were to push this into production mode, we would want to bring these files into our app locally.

In the body section of our template we are creating our top level DIV that will contain all the functionality for our mobile site:

<div data-theme="b" class="ui-page-active" data-role="page">
   <%= yield %>
</div>

There are a few things to point out in this DIV. First, we are going to use one of the pre-defined jQuery Mobile themes for this site. The theme we have chosen is called "Theme B." By placing the attribute data-theme="b" in our top-level DIV, we are assigning that element to inherit the styles for Theme B. To see all of the default theme options for jQuery Mobile you can visit the following URL: http://jquerymobile.com/demos/1.0a4.1/#docs/api/themes.html

The top-level elements of all jQuery Mobile apps are called "pages." To define a page, we set the attribute data-role="page" on an element. In our app, we are going to define only one page and then load all subsequent pages via Ajax calls. However, if we had an essentially static site, we could define multiple elements of data-role="page" all at once. We could then create simple links that would navigate these "pages" and perform nice looking transitions between them. The primary page when the browser loads the site should have an "active" state. In this case, since we are only displaying a single page element, this is not that important. However, for illustrative purposes we are assigning the class "ui-page-active" to denote that this DIV is the one that should be displayed when the browser loads the site.

The next step is to create our views. We'll start with our index.html.erb view:

<div data-role="header" data-theme="b">
	<h1>Survey</h1>
</div>
<div data-role="content">
	<%= link_to "Begin Survey", question_path(Question.find(:first)), "data-role"=>"button"%>
</div>
<div data-role="footer" data-theme="b">
	<h4>Copyright 2011</h4>
</div>
Ruby Survey App

The anatomy of a jQuery Mobile page is rather simple. Each page contains 3 main sections: the header, the content, and the footer. The CSS files and javascript are designed so that with very simple HTML, you can create a dynamic, native-like experience inside a mobile website. For our header, by simply specifying the data-role="header" attribute, we have created a nice-looking header bar with a gradient background that is theme specific. We will go into more options on this later.

In our content section, we have added a standard HTML link with the Rails helper method of link_to. We have added the data-role="button" attribute to turn that ordinary link into a stylized button. The URL for the link is a path to the first question in our database as defined by the second parameter we are passing to the link_to method.

The interesting part about making sites with jQuery Mobile is that it attempts to mimic native-app behavior by default. Instead of this link redirecting our browser to brand new page, like a typical website, the jQuery Mobile library will actually convert it into an Ajax link that will pull content in from the server and display it inside a newly created "page" element. Once it is loaded, a callback function is called that will show a transition animation to the new page. By default this new page will "slide" in from the left. Again, jQuery Mobile has accomplished this goal by allowing the developer to create this native-like experience with no special markup or advanced javascript functionality.

Lastly, we will create a footer element of data-role="footer" to hug the bottom of our content section.

Next, we will create our show.html.erb view to display our survey question to the user:

<div data-role="header" data-theme="b">
  <h1>Survey</h1>
</div>
<div data-role="content">
  <div align="center">
	<h3><%= @question.question %></h3>
  </div>
  <ul data-role="listview">
    <% @choices.each_with_index do |c, i| %>
      <% i = i + 1 %>
      <li data-theme="c">
        <%= link_to "#{i}. #{c.choice}", answer_questions_path(:id => c.id) %>
      </li>
    <% end %>
  </ul>
</div>
<div data-role="footer" data-theme="b">
	<h4>Copyright 2011</h4>
</div>
Ruby Survey App

The format of this view is almost identical, as you can see. Inside our "content" element you'll notice that we have an unordered list tag with a data-role of "listview."

<ul data-role="listview">

When an unordered list is set to this data-role, it becomes a kind of navigation element with right arrows by default. This is a pretty common paradigm in mobile apps as it can be used for both nested menus as well as a kind of slideshow where each screen is a different card in a stack.

Inside our unordered list you'll notice that the list-item has a data-theme attribute specified:

<li data-theme="c">

This illustrates how jQuery Mobile's theming engine allows us to modify any element and assign a new theme to it. In this case, the mix of Theme B's parent element's but Theme C's list-item looks really good.

Inside the list element we are using the Rails helper method to create a link again which will effectively answer the question we are displaying. It is again interesting to note that we are not doing any special Javascript or Ajax calls here with this link. By default a simple anchor tag will load the URL specified in the href attribute into a new "page" element via Ajax and then display it to the user.

Lastly, we are going to create a view that has a thank you message once the user has completed the survey. This file is located here: app/views/questions/thankyou.html.erb.

<div data-role="header" data-theme="b">
  <a href="/" data-icon="home" rel="external">Home</a>
  <h1>Thank You!</h1>
</div>

<div data-role="content">
  <p data-role="content">
	Thanks for answering the survey! Have a Good Day! :)
  </p>
</div>
Ruby Survey App

This view is very similar to the others with one exception. The link inside the "header" element has a rel="external" attribute which effectively blocks jQuery Mobile from changing the standard link into an Ajax loader. Putting rel="external" inside an anchor tag will force the link to behave normally and fully redirect the browser when clicked.

You may have notice in the screenshots that when a user answers a question, they are presented with the next question immediately. By default, jQuery Mobile places a back button inside the "header" element that hugs the left side of the screen. jQuery Mobile has a sophisticated method of determining a user's path or history through the app. Hitting the back button will move the user back to a new "page" in the app that has been dynamically loaded via the Ajax call.

By placing this link inside the "header" element, it creates an interesting feature override. Links that are placed to the left of the h1 title tag will replace the back button. Since we are at the end of the survey on this screen, we don't want the user traveling backwards through the questions again. This home link will fully redirect the browser back to the starting page so that the user can answer the survey questions again.

I encourage everyone to take a look at the demos and documentation link on the jQuery Mobile website for info on the concepts covered here: jQuery Mobile Documentation

And there we have it! I hope you enjoyed this tutorial series on making a mobile web application with Ruby on Rails and jQuery Mobile.

Related Posts
  • Web Design
    Case Studies
    How They Did It: Typekit's New HomepageTypekit retina
    Typekit recently redesigned their homepage with some new services in mind. When Typekit joined Adobe, they set out to bring us a new way to handle fonts on the web. Not only did they create a fairly simple way to embed fonts on the web, but they have now officially launched a desktop sync option, which allows Creative Cloud subscribers to sync fonts to their computer directly from Typekit. This has been in a beta form for a while now, and provides a much easier route to local fonts than finding them elsewhere!Read More…
  • Web Design
    UX
    Walk Users Through Your Website With Bootstrap TourTour retina
    When you have a web application which requires some getting used to from your users, a walkthrough of the interface is in order. Creating a walkthrough directly on top of the interface makes things very clear, so that's what we're going to build, using Bootstrap Tour.Read More…
  • Code
    Theme Development
    Creating a WordPress Theme From Static HTML: Creating Template FilesCreating wordpress theme from html 400
    In the first part of this series, I showed you how to prepare your HTML and CSS files for WordPress, ensuring the structure would work, the code was valid and that the correct classes were being used. In this tutorial you'll learn how to take your index.html file and split it up into a set of template files for use by WordPress.Read More…
  • Web Design
    HTML/CSS
    The Truth About Multiple H1 Tags in the HTML5 EraH1 retina
    Whether you're a webmaster or a web designer, there's a question you've most likely either asked or answered many times over the years. That question is, "How many <h1> tags can I use per page, and how exactly should I implement them?"Read More…
  • Code
    Theme Development
    Creating a WordPress Theme From Static HTML: Preparing the MarkupCreating wordpress theme from html 400
    Last year I did a small (and admittedly very un-scientific) survey among other WordPress developers. What I wanted to know was this: When they built their first WordPress theme, how did they do it? Did they hack an existing theme or did they start with their own static HTML and turn it into a theme? The majority of people I spoke to used the second approach - they were all experienced frontend developers who had built sites using HTML and CSS, and found it easiest to take their existing HTML files and convert them to a theme. Two of the people I spoke to were lecturers or teachers, and told me that this is the approach they use with students. So in this series I'm going to show you how to do just that.Read More…
  • Code
    PhoneGap
    PhoneGap: Build a Feed Reader - Project StructurePhonegap feed reader@2x
    Although not specifically created to work together, jQuery Mobile and Cordova (also known as PhoneGap) are a very powerful duo to create hybrid, mobile apps. This series will teach you how to develop a feed reader using web technologies and these two frameworks. Over the course of this series, you'll also become familiar with the Cordova Connection and Storage Core Plugins and the Google Feed API.Read More…