Статьи

Создайте мобильное приложение jQuery Survey: логика и интерфейс приложения

Добро пожаловать во вторую часть серии руководств по созданию веб-приложения для викторин или опросов с помощью jQuery Mobile и Ruby on Rails. В этой части руководства мы собираемся создать дружественный для мобильных устройств веб-интерфейс с jQuery Mobile, чтобы наш опрос можно было легко провести на любом совместимом с HTML5 смартфоне. Это можно сделать очень легко, так как jQuery Mobile поставляется с предопределенными шаблонами CSS, которые отлично смотрятся в мобильных браузерах, а также являются отличной библиотекой javascript, которая помогает разработчикам создавать «приложения» для мобильных веб-сайтов.

Перед тем, как мы начнем, я включил задание бонусного рейка в пример кода для этой части руководства, которая генерирует некоторые примеры вопросов. Чтобы запустить это, просто выполните это в командной строке внутри каталога приложения Rails:

1
rake setup:survey

Исходный код этой задачи находится в lib / tasks / setup.rake.

Мы начнем с генерации действия show для нашего контроллера вопросов в app / controllers / questions_controller.rb .

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

Наше шоу здесь очень простое. Мы загружаем вопрос из базы данных по его идентификатору. Мы сохраняем выбор вопроса в переменной экземпляра для последующего доступа, на наш взгляд. Вы заметите, что, поскольку мы устанавливаем отношение has_many между вопросами и вариантами ответов, мы «автоматически» получаем удобный способ получения всех вариантов ответа на вопрос, вызывая «@ question.choices». По умолчанию Rails загрузит наше представление из файла show.html.erb, который мы создадим позже.

Далее, давайте создадим действие «ответ» внутри нашего контроллера вопросов, который будет принимать ответ пользователя на вопрос и сохранять его в базе данных.

01
02
03
04
05
06
07
08
09
10
11
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

Как мы объясняли ранее, когда создавали таблицу для хранения ответов, ответ — это просто комбинация идентификатора вопроса и идентификатора выбора. Поскольку у нас нет концепции пользователя в этой системе, мы просто собираемся хранить ответы и позже рассмотрим результаты. Давайте разберемся с этим:

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

В приведенном выше коде мы находим выбор в базе данных по ее идентификатору. Затем мы создаем объект ответа, который состоит из question_id и идентификатора, который мы получаем из объекта выбора.

1
2
3
4
5
6
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

После того, как пользователь ответит на вопрос, у нас есть решение, которое определит, что показать пользователю. Если пользователь ответил на последний вопрос, который мы сохранили в базе данных (который мы можем получить с помощью «Question.last»), мы будем отображать наше представление «спасибо за заполнение опроса». Если это не последний вопрос, мы найдем вопрос с «позицией» позиции текущего вопроса плюс 1. Затем мы перенаправим на действие show для этого вопроса с помощью вспомогательного метода RESTful rails метода question_path . Для получения дополнительной информации о создании контроллеров RESTful rails выполните поиск в Google по запросу «RESTful Rails 3» и прочитайте некоторые статьи, которые опубликовали люди.

В мире RESTful не существует такой вещи как действие ответа, поэтому мы должны добавить это в наш файл config / rout.rb.

Просто замените эту строку:

1
resources :questions

с этим:

1
2
3
4
5
resources :questions do
    collection do
      get :answer
    end
end

В настоящее время, если пользователь перейдет по корневому URL нашего сервера, ему будет выдана ошибка. Чтобы предотвратить это, мы также добавим эту корневую опцию в наш файл config / rout.rb:

1
root :to => «questions#index»

Эта строка будет направлять запрос корневого URL на действие index контроллера вопроса. Хотя мы не определили фактическое действие индекса, по умолчанию Rails загрузит файл index.html.erb в качестве представления. Мы собираемся создать этот файл позже.

Теперь, когда наша работа с Rails по существу завершена, мы собираемся приступить к созданию представлений, которые будут использовать инфраструктуру jQuery Mobile. Мы начнем с создания глобального макета для нашего шаблона в app / views / layouts / application.html.erb.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
<!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>

В разделе head вы заметите, что мы загружаем 2 файла jQuery Mobile с сайта jQuery: 1 файл CSS и 1 файл JS. Это хорошо для режима разработки, но если бы мы перешли в рабочий режим, мы бы хотели добавить эти файлы в наше приложение локально.

В разделе тела нашего шаблона мы создаем наш DIV верхнего уровня, который будет содержать все функциональные возможности для нашего мобильного сайта:

1
2
3
<div data-theme=»b» class=»ui-page-active» data-role=»page»>
   <%= yield %>
</div>

Есть несколько вещей, на которые следует обратить внимание в этом DIV. Во-первых, мы собираемся использовать одну из предопределенных тем jQuery Mobile для этого сайта. Тема, которую мы выбрали, называется «Тема Б.» Размещая атрибут data-theme = «b» в нашем DIV верхнего уровня, мы назначаем этот элемент для наследования стилей для темы B. Чтобы увидеть все параметры темы по умолчанию для jQuery Mobile, вы можете посетить следующий URL: http : //jquerymobile.com/demos/1.0a4.1/#docs/api/themes.html

Элементы верхнего уровня всех приложений jQuery Mobile называются «страницами». Чтобы определить страницу, мы устанавливаем атрибут data-role="page" для элемента. В нашем приложении мы собираемся определить только одну страницу, а затем загрузить все последующие страницы с помощью вызовов Ajax. Однако, если бы у нас был практически статический сайт, мы могли бы определить несколько элементов data-role="page" одновременно. Затем мы можем создать простые ссылки, которые будут перемещаться по этим «страницам» и выполнять красивый переход между ними. Основная страница, когда браузер загружает сайт, должна иметь «активное» состояние. В этом случае, поскольку мы отображаем только один элемент страницы, это не так важно. Однако в иллюстративных целях мы назначаем класс "ui-page-active" чтобы обозначить, что именно этот DIV должен отображаться при загрузке сайта браузером.

Следующим шагом является создание наших представлений. Начнем с нашего представления index.html.erb:

1
2
3
4
5
6
7
8
9
<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

Анатомия страницы jQuery Mobile довольно проста. Каждая страница содержит 3 основных раздела: верхний колонтитул, содержание и нижний колонтитул. Файлы CSS и javascript спроектированы таким образом, чтобы с очень простым HTML вы могли создавать динамические, похожие на нативные ощущения внутри мобильного веб-сайта. Для нашего заголовка, просто указав атрибут data-role="header" , мы создали красивую панель заголовка с градиентным фоном, который зависит от темы. Мы поговорим об этом позже.

В нашем разделе контента мы добавили стандартную HTML-ссылку с помощью вспомогательного метода Rails link_to . Мы добавили атрибут data-role="button" чтобы превратить эту обычную ссылку в стилизованную кнопку. URL для ссылки — это путь к первому вопросу в нашей базе данных, который определяется вторым параметром, который мы link_to методу link_to .

Интересной частью создания сайтов с помощью jQuery Mobile является то, что он пытается имитировать поведение нативных приложений по умолчанию. Вместо этой ссылки, перенаправляющей наш браузер на совершенно новую страницу, как на типичном веб-сайте, библиотека jQuery Mobile фактически преобразует ее в ссылку Ajax, которая будет извлекать контент с сервера и отображать его во вновь созданном элементе «страница». После загрузки вызывается функция обратного вызова, которая покажет анимацию перехода на новую страницу. По умолчанию эта новая страница будет «скользить» слева. Опять же, jQuery Mobile выполнил эту задачу, позволив разработчику создать подобный нативу опыт без специальной разметки или расширенной функциональности JavaScript.

Наконец, мы создадим элемент нижнего колонтитула data-role="footer" чтобы обнять нижнюю часть нашего раздела контента.

Далее мы создадим наше представление show.html.erb для отображения нашего вопроса об опросе для пользователя:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
<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

Как вы можете видеть, формат этого представления практически идентичен. Внутри нашего элемента «content» вы заметите, что у нас есть неупорядоченный тег list с ролью данных «listview».

1
<ul data-role=»listview»>

Когда неупорядоченному списку присваивается эта роль данных, он становится своего рода элементом навигации с стрелками вправо по умолчанию. Это довольно распространенная парадигма в мобильных приложениях, поскольку она может использоваться как для вложенных меню, так и для своего рода слайд-шоу, где каждый экран представляет собой отдельную карту в стопке.

Внутри нашего неупорядоченного списка вы заметите, что в элементе списка указан атрибут data-theme:

1
<li data-theme=»c»>

Это показывает, как движок тем jQuery Mobile позволяет нам изменять любой элемент и назначать ему новую тему. В этом случае микс родительского элемента Темы B, но элемент списка Темы C выглядит действительно хорошо.

Внутри элемента списка мы используем вспомогательный метод Rails, чтобы снова создать ссылку, которая эффективно ответит на вопрос, который мы отображаем. Еще раз интересно отметить, что мы не делаем никаких специальных вызовов Javascript или Ajax здесь с этой ссылкой. По умолчанию простой тег привязки будет загружать URL, указанный в атрибуте href, в новый элемент «page» через Ajax, а затем отображать его для пользователя.

Наконец, мы собираемся создать представление, которое будет содержать сообщение с благодарностью, как только пользователь завершит опрос. Этот файл находится здесь: app / views / questions / thankyou.html.erb.

01
02
03
04
05
06
07
08
09
10
<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!
  </p>
</div>
Приложение Ruby Survey

Эта точка зрения очень похожа на другие, за одним исключением. Ссылка внутри элемента header имеет атрибут rel = «external», который эффективно блокирует jQuery Mobile от превращения стандартной ссылки в загрузчик Ajax. Помещение rel="external" внутри тега привязки заставит ссылку вести себя нормально и полностью перенаправит браузер при нажатии.

Вы можете заметить на скриншотах, что когда пользователь отвечает на вопрос, ему сразу же задают следующий вопрос. По умолчанию jQuery Mobile размещает кнопку «Назад» внутри элемента «заголовок», который обнимает левую часть экрана. В jQuery Mobile есть сложный метод определения пути или истории пользователя через приложение. Нажатие кнопки «Назад» переместит пользователя на новую «страницу» в приложении, которая была динамически загружена с помощью вызова Ajax.

Помещая эту ссылку внутри элемента header, он создает интересное переопределение функции. Ссылки, расположенные слева от тега заголовка h1, заменят кнопку возврата. Поскольку мы находимся в конце опроса на этом экране, мы не хотим, чтобы пользователь снова возвращался к вопросам. Эта домашняя ссылка полностью перенаправит браузер обратно на стартовую страницу, чтобы пользователь мог снова ответить на вопросы опроса.

Я рекомендую всем взглянуть на ссылку с демонстрациями и документацией на веб-сайте jQuery Mobile для получения информации о концепциях, представленных здесь: Документация jQuery Mobile

И там у нас это есть! Надеюсь, вам понравилась серия руководств по созданию мобильного веб-приложения на Ruby on Rails и jQuery Mobile.