Реализация оперативного просмотра

Проблема
Хотелось бы дать пользователям возможность оперативного просмотра данных в процессе редактирования, чтобы не получалось так, что после отправки из фор­мы данные появились в испорченном формате (когда речь идет, к примеру, о дневниковой записи, которую собираются выставить на всеобщее обозрение).


Решение
Оперативный просмотр легко осуществить с помощью встроенных в Rails по­мощников JavaScript. Для данного рецепта мы обойдемся созданием оперативно­го просмотра простейшей формы, предназначенной для создания дневниковой записи.
Первым делом при создании любого Rails-эффекта, связанного с использова­нием Ajax, нужно обеспечить включение в код нужных JavaScript-библиотек. Для достижения эффекта оперативного просмотра требуется включить лишь одну библиотеку — Prototype. Я рекомендую сделать это в главном шаблоне приложе­ния (в нашем случае — в файле layouts/standard.rhtml):

  1. <html>
  2. <head>
  3. <%= javascript_include_tag "prototype" %>
  4. </head>
  5. <body>
  6. ….
  7. </body>
  8. </html>

Теперь, обеспечив загрузку требуемых JavaScript-библиотек, нужно создать модель и контроллер, которые будут поддерживать ввод дневниковых записей. Мы будем вызывать класс модели Entry, передавая ему свойства title и body. Если хотите продолжать работу, не определяя соответствующей таблицы Active Record, то файл app/models/entry.rb должен приобрести следующий вид:

  1. class Entry
  2. attr_accessor :title, :body
  3. end

Контроллер будет называться DiaryController. Мы создадим его в файле app/control-lers/diary_controller.rb. Давайте придерживаться основ, и назовем действие по созда­нию новой записи new( ):

  1. def new
  2. @entry = Entry.new
  3. end

Теперь настал черед самой функции. В представлении, созданном для этого действия, и будет твориться все наше волшебство. Создайте файл app/views/diary/new.rhtml и придайте ему следующий вид:

  1. <% form_tag ({:action => "save"}, :id=> "entry-form") do %>
  2. <%= text_field :entry, :title %> <br />
  3. <%= text_area :entry, :body %> <br />
  4. <%= submit_tag "Save" %>
  5. <% end -%>
  6.  
  7. <%= observe_form "entry-form",
  8. :frequency=>2,
  9. :update=>"live-preview",
  10. :complete=>"Element.show(‘live-preview’)",
  11. :url=> {:action => "preview"} %>
  12.  
  13. <div id="live-preview" style="display:none; border:1px solid"></div>

Мы создали стандартную, универсальную форму с id, имеющим значение entry-form, поэтому на нее можно ссылаться из всего остального кода. За опреде­лением формы следует вызов помощника observe_form( ), который генерирует тре­буемый JavaScript-код для опроса каждого элемента формы, выведенной на стра­нице (ссылаясь на нее по id), и поиска изменений. Опрос будет вестись через интервалы времени (в секундах), заданные параметром frequency. Как только бу­дут замечены изменения, на URL, определенный параметром :url, будет направлен вызов, которому в качестве параметров будут переданы данные формы. Параметр :update определяет HTML-элемент (опять же по его id), который будет обновлен результатами URL-вызова. В данном случае содержимое элемента <div>, который используется для оперативного просмотра, будет обновлено тем, что в конечном счете будет отправлено в результате вызова действия preview( ).
Чтобы сделать элемент оперативного просмотра невидимым при первоначаль­ной загрузке страницы, мы воспользовались встроенным CSS. Пока пользователь не введет какие-либо данные, в элементе оперативного просмотра нечего будет показывать. Параметр :complete, передаваемый помощнику observe_form( ), предписы­вает после завершения вызова действия preview( ) выполнение фрагмента Java­Script, который включит отображение элемента оперативного просмотра.
Если в оперативном просмотре нужно отобразить всего лишь одно поле, то мы можем взамен воспользоваться помощником observe_field( ).
Теперь осталось только обеспечить выполнение действия preview( ). Вот его код, взятый из контроллера:

  1. def preview
  2. render :layout => false
  3. end

Единственная задача, выполняемая кодом действия, — это «закорачивание» обычной схемы отправки данных, используемой в приложении. Поскольку мы собираемся обновлять элемент оперативного просмотра на странице создания еже­дневных дневниковых записей и использовать для этого лишь результаты, воз­вращаемые действием preview( ), возвращать целиком всю HTML-страницу не тре­буется. Нам нужен лишь отрывок, имеющий определенный смысл в составе более объемного содержимого экрана.
Представление, предназначенное для действия preview( ), находится в файле app/views/diary/preview.rhtml и должно иметь следующий вид:

  1. <h2>Preview:</h2><br />
  2. <h3><%= params[:entry][:title] %></h3>
  3. <%= textilize params[:entry][:body] %>

Вот и все! Это представление использует HTML-заголовок для названия за­писи, а затем, используя метод textilize( ), генерирует HTML-выход. Внутри этого метода используется библиотека RedCloth для преобразования простой тексто­вой разметки в HTML.
Теперь можно загрузить форму ввода дневниковой записи и посмотреть, как обычный текст преобразуется в HTML еще до того, как будет сделан щелчок на кнопке Save.

P.S.
Параметру frequency, использующемуся в методах observe_field( ) и observe_form( ), можно присвоить нулевое или отрицательное значение, что приведет к обзору по­ля в реальном масштабе времени. Казалось бы, что может быть плохого в мгно­венной реакции пользовательского интерфейса, но на самом деле она будет по­давлена, не говоря уже о дополнительной тяжелой нагрузке на серверы. При отслеживании изменений в реальном масштабе времени каждое изменение вызо­вет отправку запроса на сервер, от которого нужно дождаться результата, чтобы увидеть обновление на экране. Изменения становятся в очередь, в результате че­го обновления оперативного просмотра медленно следуют за ними, ожидая оче­редного перехвата.

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

Comments (3)

  1. Как связаться с автором?

  2. можете написать мне на мыло leopard_ne[at]inbox.ru ([at] замените на собаку)

  3. Так зачитался, что пропустил любимую передачу)