#ruby-on-rails #ruby #acts-as-taggable-on #html-select
#ruby-on-rails #ruby #действует как тегируемый #html-выберите
Вопрос:
В моем приложении rails у меня есть две модели: Team
и Post
, каждая Post
может иметь несколько тегов, и у каждой Team
может быть много Posts
. В действии show моей команды я хочу представить select_tag
, с помощью которого пользователь может выбрать один тег из всех созданных тегов для своих сообщений. Когда пользователь выбирает тег с помощью select_tag
, я хочу показывать только сообщения, которые также имеют этот тег. Я использую Acts as Taggable On
драгоценный камень.
В настоящее время у меня есть это (я использую HAML
):
= select_tag "tags", options_from_collection_for_select(@posts, "tags", :tag_list)
Это дает мне выбор, который выглядит примерно так:
[row 1] Tag 1
[row 2] Tag 1, Tag 2
[...]
Мне нужен список выбора, который выглядит следующим образом:
[row 1] Tag 1
[row 2] Tag 2
[row 3] Tag 3
[...]
Я мало что использовал select_tag
. Можно ли как-то сделать то, что я хочу? И как это будет выглядеть?
Ответ №1:
В этом есть два аспекта: отображение правильного списка тегов и фильтрация сообщений по выбранному тегу.
Отображение правильного списка тегов
Вы хотите создать <select>
элемент, который содержит теги только для сообщений этой команды. options_from_collection_for_select
принимает 3 параметра: коллекцию, метод для получения значения для каждого параметра и метод для получения имени каждого параметра. Таким образом, вы должны передавать набор тегов options_from_collection_for_select
, а не набор сообщений. И этот набор тегов не должен содержать все теги — только те, которые относятся к сообщениям вашей команды. К счастью, acts_as_taggable_on
предоставляет метод для этого, чтобы вы могли создавать облака тегов.
Итак, вы собираетесь обновить свой контроллер, чтобы он выглядел примерно так:
class TeamController < ApplicationController
def show
@team = Team.find params[:id]
@posts = team.posts
@team_tags = team.posts.tag_counts_on(:tags)
end
end
И обновите свой вид, чтобы использовать @team_tags
вместо этого:
= select_tag "tag", options_from_collection_for_select(@team_tags, 'id', 'name')
Это должно дать вам поле выбора, заполненное тегами, специфичными для этой команды.
Фильтрация сообщений по тегу
Теперь у нас есть правильный список тегов, мы хотим иметь возможность фильтровать сообщения по тегу. Этот <select>
тег должен быть внутри формы; вы можете либо создать новое действие контроллера, которое обрабатывает эту форму, либо соответствующим образом адаптировать свой show
метод. Последнее немного сложнее, поэтому я использовал этот подход здесь. Давайте обновим ваше представление, чтобы сделать предположения более ясными:
= form_tag team_path(@team), method: 'get', class: 'tag_form' do
= select_tag "tag", options_from_collection_for_select(@team_tags, 'id', 'name', @tag)
= submit_tag "Filter posts"
И обновите действие контроллера для фильтрации сообщений, если у нас есть параметр ‘tags’:
class TeamController < ApplicationController
def show
@team = Team.find params[:id]
@team_tags = team.posts.tag_counts_on(:tags)
if params[:tag]
@tag = Tag.find params[:tag]
@posts = team.posts.tagged_with @tag.name
else
@tag = nil
@posts = team.posts
end
end
end
Обратите внимание, что мы передаем выбранный тег в качестве переменной экземпляра @tag
и используем его в нашем options_from_collection_for_select
, чтобы выбрать правильный тег, если мы выбрали фильтр.
Глазурь на торте
Вы можете использовать немного jQuery и CoffeeScript, чтобы скрыть кнопку отправки и сделать фильтрацию автоматически, когда пользователь выбирает новый тег, что немного улучшит ваш пользовательский интерфейс. Добавьте это в app/assets/javascripts/teams.js.coffee
:
$ ->
$forms = $('.tag_form')
$forms.each (i, el) ->
$('input[type=submit]', el).hide()
$('select', el).on 'change', (ev) ->
$(el).submit()
Это работает путем поиска всех элементов с классом tag_form
, удаляет все <input type="submit">
элементы внутри каждого из них, а затем прослушивает события изменения выбранных элементов в этих формах.
Комментарии:
1. Спасибо, отличный ответ! Я заставил его работать, пришлось внести две незначительные модификации. Мне пришлось добавить
do
вform_tag
строку. И чтобы получить правильные сообщения, я должен был сделать:@posts = @team.posts.tagged_with(@tag.name)
.2. Спасибо за исправления! Я обновил свой пост этим, чтобы будущим людям не приходилось исправлять это отдельно. 🙂
3. Один дополнительный вопрос, знаете ли вы, можно ли использовать
select_tag
с вложенными ресурсами. Итак, я бы получил URL-адреса, которые выглядят примерно так:teams/1/tags/tag-name
. С ресурсом, который выглядит следующим образом:resources :teams do get 'tags/:tag', to: "teams#show", as: :tag end
. Как будетselect_tag
выглядеть строка в этом случае?