Обновление нескольких элементов при помощи одного Ajax-запроса

Проблема
Вы уже видели, как работающие с формой Ajax-пoмoщники позволяют обнов­лять часть рабочей страницы результатами удаленного действия. Для определе­ния идентификатора НТМL-элемента, который должен быть обновлен данными, полученными от удаленного действия, многие Аjах-действия используют пара­метр :update.
Для большинства ситуаций этот механизм очень удобен и вполне достаточен. Если к списку нужно добавить какую-нибудь запись, то полученной с сервера из­мененной версией обновляется только HTML этого списка. То же самое будет происходить и при местном редактировании формы.
Но если нужно одним щелчком или действием на странице провести обновле­ние нескольких, заведомо разобщенных элементов, этот шаблон уже не сработает. Возможность решить эту задачу, использовав параметр : update, довольно при­зрачна и проблематична.


Решение
В Rails 1.1 появился новый тип шаблонов, названный Remote JavaScript, или RJS. Так же как Builder-шаблоны, файлы которых имели расширение .rxml, шаблоны с расширением файловых имен .rjs автоматически обрабатывались как RJS-шаб-лоны.
RJS предоставляет простые, лаконичные Ruby-методы, генерирующие много­словный код JavaScript. Вы вызываете метод

  1. page.hide   ‘element-id’

и RJS генерирует JavaScript, устанавливающий режим отображения названного элемента в попе, а затем направляет этот JavaScript браузеру. Содержимое возвра­щается браузеру с указанием в Content-type типа text/javascript. Распространяемая вме­сте с Rails JavaScript-библиотека Prototype распознает этот Content-type и вызывает JavaScript-функцию eval( ) с возвращенным содержимым.
Чтобы увидеть все это в действии, давайте взглянем на небольшой пример. Предположим, у нас уже есть сгенерированное приложение, для которого мы сге­нерируем новый контроллер, с которым и будем иметь дело:

> ruby script/generate controller AjaxFun

exists app/controllers/

Затем в файле index.rhtml мы создадим для этого контроллера простое представ­ление, которое станет испытательным полигоном для нашего Ajax. С учетом важ­ности идентификатора HTML-элемента содержимое файла index.rhtml должно вы­глядеть следующим образом:

  1. <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
  2. <html>
  3. <head>
  4. <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
  5. <title>Проба Ajax</title>
  6. <%= javascript_include_tag :defaults %>
  7. </head>
  8. <body>
  9. <h2 id="header">Проба Ajax</h2>
  10. <div>
  11. Эта страница первоночально была загружена в <%= Time.now %>
  12. </div>
  13. <div>
  14. Эта страница была обновленна в <span id="time_updated"><%= Time.now %></span>
  15. </div>
  16. <ul id="the_list">
  17. <li>Изначально первый элемент</li>
  18. <li>Другой элемент</li>
  19. <li id="item_to_remove">Этот элемент будет удален</li>
  20. </ul>
  21. <div id="initially_hidden" style="display:none;">
  22. Этот текст изначально не видим
  23. </div>
  24. <%= link_to_remote "Магия Ajax", :url => {:action => "change"} %><br />
  25. </body>
  26. </html>

Мы скрупулезно пометили элементы, подлежащие динамическому обновле­нию HTML-атрибутом ID. Ссылка на удаленный ресурс, расположенная в конце страницы, запускает запрос XMLHttpRequest, направляемый методу контроллера change( ). Именно с этой ссылки и начнется наше развлечение. Заметьте, что ссыл­ке link_to_remote( ) не передан параметр update. Давайте сначала взглянем на кон­троллер:

  1. class AjaxFunController < ApplicationController
  2. def change
  3. @rails_version = Rails::VERSION::STRING
  4. end
  5. end

В нем всего лишь устанавливается значение переменной экземпляра под на­званием @rails_version, которая будет востребована в представлении. Вся настоя­щая работа будет происходить в представлении, созданном для этого действия, размещенном в файле change.rjs:

  1. page.replace_html ‘time_updated’, Time.now.to_s
  2. page.visual_effect :shake, ‘time_updated’
  3.  
  4. page.insert_html :top, ‘the_list’, ‘<li>Король вершины</li>’
  5. page.visual_effect :highlight, ‘the_list’
  6.  
  7. page.show ‘initially_hidden’
  8.  
  9. page.delay(3) do
  10. page.alert @rails_version
  11. end
  12.  
  13. page.remove ‘item_to_remove’

Можно заметить, что RJS в неявном виде поставляет объект под названием page, который обеспечивает все методы генерации JavaScript. В первой строке содержи­мое HTML span-тега с идентификатором time-updated заменяется показанием теку­щего времени. В следующей строке обеспечивается предупреждение пользователя легкой вибрацией изображения, показывающей, что время было обновлено.
В четвертой строке обеспечивается вставка нового значения списка в его не предписанный элемент, за этим следует вызов эффекта под названием 37sig­nals-coined Yellow Fade Technique. Учтите, что каждый из методов, insert_html() и replace_html( ), может воспринять либо строку, как в данном примере, либо те же параметры, которые воспринимаются методом render( ). Поэтому в страницу мож­но, к примеру, вставить результат отправки фрагментарного шаблона представле­ния. В седьмой строке вызывается отображение скрытых элементов страницы.
Противоположным действием обладает метод hide( ), который не нужно путать с методом remove( ), использованным в тринадцатой строке для реального удале­ния элемента с HTML-страницы.
И наконец, в девятой строке мы используем довольно необычный метод delay( ), вызывающий появление всплывающего предупреждения JavaScript через три се­кунды после того, как страница будет загружена. Метод delay( ) генерирует Java­Script-функцию timeout, которая выполняет любой JavaScript-код, сгенерирован­ный внутри предоставленного ему блока.
Заметьте, что в методе alert( ) используется переменная экземпляра @rails_ version, значение которой было присвоено в контроллере. Переменные экземпляра и вспомогательные методы доступны в RJS-шаблонах, так же как и в любом дру­гом представлении. Уже упоминалось, что RJS-шаблоны генерируют JavaScript и отправляют его браузеру на выполнение. Конкретно для этого RJS-шаблона сгенерированный JavaScript будет выглядеть следующим образом:

  1. try {
  2.  
  3. Element.update("time_updated", "Fri Sep 05 17:11:28 +0300 2008");
  4.  
  5. new Effect.Shake("time_updated",{});
  6.  
  7. new Insertion.Top("the_list", "\u003Cli\u003E\u041a\u043e\u0440\u043e\u043b\u044c \u0432\u0435\u0440
  8.  
  9. \u0448\u0438\u043d\u044b\u003C/li\u003E");
  10.  
  11. new Effect.Highlight("the_list",{});
  12.  
  13. Element.show("initially_hidden");
  14.  
  15. setTimeout(function() {
  16.  
  17. ;
  18.  
  19. alert("2.0.2");
  20.  
  21. }, 3000);
  22.  
  23. Element.remove("item_to_remove");
  24.  
  25. } catch (e) { alert(‘RJS error:\n\n + e.toString()); alert(‘Element.update(\"time_updated\", \"Fri Sep
  26.  
  27. 05 17:11:28 +0300 2008\");\nnew Effect.Shake(\"time_updated\",{});\nnew Insertion.Top(\"the_list\",
  28.  
  29. \"\\u003Cli\\u003E\\u041a\\u043e\\u0440\\u043e\\u043b\\u044c \\u0432\\u0435\\u0440\\u0448\\u0438\\u043d
  30.  
  31. \\u044b\\u003C/li\\u003E\");\nnew Effect.Highlight(\"the_list\",{});\nElement.show(\"initially_hidden
  32.  
  33. \");\nsetTimeout(function() {\n;\nalert(\"2.0.2\");\n}, 3000);\nElement.remove(\"item_to_remove\");’
  34.  
  35. ); throw e }

Вот, собственно, и все. Проще простого. С появлением RJS Ajax перестал быть чем-то излишне сложным.

P.S.
Тип содержимого RJS-шаблона — Content-type — должен быть установлен в text/java­script. Обработчик RJS делает это за вас, но, если в приложении имеется код, кото­рый явным образом указывает Content-type, может обнаружиться, что RJS-шаблоны не работают. Если возникает подозрение, что RJS-шаблоны ровным счетом ниче­го не делают, проверьте, нет ли в приложении фильтра «после» (after) устанавли­вающего значение Content-type.
Вы должны знать еще об одной особенности применения RJS-шаблонов. По­скольку они генерируют JavaScript для выполнения в браузере, возникают труд­ности с определением ошибок. Например, если вы допустили синтаксическую ошибку в контроллере или представлении, Rails вернет свою обычную запись стека в HTML-формате. Проблема состоит в том, что код выполняется без види­мых признаков, и если код JavaScript не может быть выполнен, то в конечном счете в браузере ничего не произойдет. Поэтому, если вы уставились на безжиз­ненный экран браузера в бесконечном ожидании завершения работы усиленного RJS действия Ajax, нужно проверить регистрационные записи, чтобы понять, что запрос закончился ошибкой.

05. сентября 2008 by Alexey Vasiliev
Categories: Ajax, javascript, Ruby on Rails | Tags: , , | Один комментарий

One Comment

  1. За такие посты надо награды давать, на полном серьезе!