Rails 4 — Выберите тег из коллекции, используя его в качестве тегируемого драгоценного камня с select_tag

#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 выглядеть строка в этом случае?