Popular Posts

пятница, 13 апреля 2012 г.

Rails Autocomplete

Привет.
Сегодня рассмотрим автозаполнение (autocomplete) модная штука, с ajax. Для этого я буду использовать наработки. Помните пост, где делали теги? Вот именно для них я буду и делать автозаполнение.
И так у нас есть модель пост, с добавлеными туда тегами.
class Post < ActiveRecord::Base
    acts_as_taggable_on :tags
end
Конечно там больше кода, но важно видеть, что я не обманываю и там действительно есть теги.
Хорошо, теперь добавляем в контроллер метод для который будет отдавать все теги:
class PostsController < ApplicationController
  def get_tags
    @tags = Post.tag_counts.order('count DESC').limit(3).where('tags.name LIKE ?', "%#{params[:q]}%")
    render :get_tags, :layout => false
  end
end

Здесь простой запрос  к методу tag_counts, который автоматически генерируется acts_as_taggable_on, потом я ограничиваю количество тегов до 3-х, хотя можно и не ограничивать, и буду искать все имена тегов (tags.name), которые похожи на %params[:q]%,
это говорит о том, что искомый тег может начинаться и заканчиваться с любого символа. То есть  я буду отправлять запрос вида :q => "a", и вот эта буква "а" может встречаться в любом месте имени.
Ответ как видим не требует всего layout, потому что иначе будет отправлена в ответе вся страница со всеми файлами и прочим, нам же нужны только теги.

Добавляем, теперь этот самый шаблон страницы.
#views/posts/get_tags.html.erb
<ul>
<% @tags.each do |tag| %>
    <li><%= tag.name %></li>
<% end %>   
</ul>
Тут как видите просто, оборачиваем имя тега (name) в элементы li.
Я забыл сказать, что буду использовать Ajax.Autocompleter от Script.aculo.us. Вот, он требует такого ответа, хотя лучше ответ в json, но это тут не важно.

Затем добавляем в форму нового поста, поле для автозаполнения:
#views/posts/_form.html.erb
<%= form_for(@post) do |f| %>
  #несколько полей

<div class="field">
    <%= f.label 'Tag List' %><br />
    <%= f.text_field :tag_list, {:id => "autocomplete"} %>
    <div data-url="<%= url_for(get_tags_posts_path) %>" id="autocomplete_choices" class="autocomplete"></div>
</div>

<% end %>

Здесь я добавил атрибут data-url к полю для автозаполнения, этот url будет генерироваться автоматически, именно по этому адресу нужно обращаться для получения тегов.

Не забываем указать в ability.rb, кому можно получать теги:
#models.ability.rb
def initialize(user)
 #код

   if user.role?(:user)
        can :get_tags, Post

 #код
end
Это говорит о том, что теги могут получить только вошедшие в систему пользователи.
Так теперь добавим роутинг.
#config/routes.rb
resources :posts do
  #....

   post :get_tags, :on => :collection
end

Всё.

Осталось написать небольшой js-файл для обработки всего.
Сделайте файл в assets/javascripts/autocomplete.js
Вот нужный код

$(document).observe('dom:loaded', function() {
 var token = $$('input[name=authenticity_token]')[0];
 var url = $('autocomplete_choices').readAttribute('data-url');
 new Ajax.Autocompleter("autocomplete", 
                           "autocomplete_choices", url, 
                           {
                            paramName: "q",
                            tokens: [','],
                            requestHeaders: {'X-CSRF-Token': token.getValue()}
                           });

});

Переменная token нужна для предъявления серверу токена пользователя, это для защиты.
Потом мы читаем data-url, который добавили, помните? Это адрес для отправки запроса.
Потом сам Ajax.Autocompleter. У него первый аргумент -- это поле из которого нужно брать символы, второй это поле в которое будет писаться ответ, а потом идут параметры.
paramName: "q", q - это то, что будет в params, которое читаем в контроллере.
tokens: [','], это важный параметр, указывает разделитель между тегами, если его не будет, то будет на автозаполнении только первый самый тег, остальных нет.
requestHeaders, указывает дополнительный заголовок для сервера, тут отправляем token.getValue(), это значение находится в скрытом поле. Можете в исходном коде страницы посмотреть. Без этого заголовка, сервер не принимает нас, и перенаправляет на страницу входа, можете попробывать.
Теперь, нужно же добавить файл, так как автозаполнение нужно будет всего на двух страницах, странице создания и редактирования пост, то добавляем в _form.html.erb и всё.

<%= javascript_include_tag "autocomplete" %>

<%= form_for(@post) do |f| %>

#ваш код
Конечно это вставит код посреди страницы, но это не страшно.
Так последнее действие это добавление в загрузку нужных файлов.
#assets/javascripts/application.js

//
//= require scriptaculous/lib/prototype
//= require scriptaculous/src/effects
//= require scriptaculous/src/controls

Как видно я зугружая библиотеку прототип и нужные файлы из script.aculo.us -- эффекты и контролы. Поэтому разместите папку scriptaculous в assets/javascripts.

Ах да не забудьте добавить стили:

div.autocomplete {
  position:absolute;
  width:250px;
  background-color:white;
  border:1px solid #888;
  margin:0;
  padding:0;
}
div.autocomplete ul {
  list-style-type:none;
  margin:0;
  padding:0;
}
div.autocomplete ul li.selected { background-color: #ffb;}
div.autocomplete ul li {
  list-style-type:none;
  display:block;
  margin:0;
  padding:2px;
  height:32px;
  cursor:pointer;
} 

Ну или напишите свои, эти со страницы autocomplete.


Комментариев нет:

Отправить комментарий