LoginSignup
6
7

More than 5 years have passed since last update.

acts-as-taggable-on:チェックボックスでタグ更新

Posted at

acts-as-taggable-onはタグ付けするのに便利なgemなのですが、登録済みのタグをチェックボックスで表示してチェックを外すと削除できるようにしようとしたら色々手こずったのでやり方を公開しようと思います。

環境

  • rails 4.1.10
  • acts-as-taggable-on 3.4.2

acts-as-taggable-onの基本

タグの追加と削除の方法

# タグの追加
@resource.tag_list.add('hoge,huga')
# タグの削除
@resource.tag_list.remove('hoge,huga')

やろうとしたこと

  • 追加するタグはtext_fieldに記述 。コンマでもスペースでも区切れるようにする。
  • 登録済みのタグはチェックボックス付きの一覧にし、チェックボックスを外したらタグを外すようにする

まずは失敗例

チェックボックスでタグを配列として取得し、tag_listに上書きすればいいじゃんと思い、やってみたら失敗しました。

@resource.tag_list = params[:tag_list]
# => Error: undefined method `new' for [" ", " ", ","]:Array

上記のようにtag_listに直接書き込もうとしたらエラーが発生しました。
※以前はこの方法でやってたらできてたのに、ある日突然バグるようになりました。acts-as-taggable-onのバージョンアップによるものか、railsのバージョンアップによるものかはわかりません……。

成功したコード

複数のモデルに同じタグ付けしたので、controllerのconcernsにモジュールを作りました。
update_tagsというアクションでタグの更新を行います。

ルーティング

Itemというモデルにタグ更新アクションを追加します。
またrails4より更新系のアクションはputではなくpatchが推奨になりましたのでpatchを指定します。

routes.rb
resources :items do
    patch 'update_tags', on: :member
end

View

ポイントはチェックボックスのmultipleをtrueにすることと、tagがtag_listに含まれていたらチェックするようにすることです。

views/tags/_form.html.slim
= form_for @resource, url: {action: 'update_tags', method: :patch} do |f|
    = f.text_field :add_tags, :value => nil, :class => 'add_tag'
    - @resource.tag_counts.each do |tag|
        = f.check_box :tag_list, 
            { multiple: true, checked: @resource.tag_list.include?(tag.to_s) }, tag, nil
        = f.label tag, tag, class: 'tag'
        = f.submit

Controller

まずconcernsにTagManagerというモジュールを作り、そこにupdate_tagsというアクションを設定しました。

controllers/concerns/tag_manager.rb
module TagManager
  extend ActiveSupport::Concern

  included do
    def update_tags
      # チェックボックスの中身
      tag_list = params[model_name.downcase][:tag_list]

      # 消されたタグ = 元のタグ - チェックされたタグ
      remove_tags = @resource.tag_list - tag_list
      @resource.tag_list.remove(remove_tags) if remove_tags

      # 追加されたタグ
      add_tags = params[model_name.downcase][:add_tags]
      if add_tags
         # default_parserはコンマですが、半角/全角スペースもタグの区切りとみなします
        add_tags_array = add_tags.gsub(/[[:blank:]]+/, ',').split(',')
        @resource.tag_list.add(add_tags_array)
      end

      if @resource.save
        flash[:notice] = 'タグを更新しました'
      else
        flash[:notice] = 'タグの更新に失敗しました'
      end
      redirect_to :back
    end
  end
end

Itemコントローラーではポイントとなる点だけ抜粋してコードを載せます。

ポイント

  • concernsに作ったTagManagerを読み込み。
  • strong_parametersでadd_tagsとtag_listを追加。
    tag_listはチェックボックスの内容を配列で取るのでtag_list: []とします。
items_controller.rb
class ItemsController < ApplicationController
    include TagManager

*** 略 ***

private
    def item_params
        params.require(:item).permit(
            :name, :category_id, :subcategory_id, :authors, :add_tags, tag_list: [])
    end
end

今回はconcernsにモジュールを作って各resourceに対してアクションという方法を取ったのですが、これ書きながらtags_controllerとかに一つ新しいアクションを追加する方が良いような気もしてきました。
ま、とりあえず今回はconcernsの勉強も兼ねてこの形でということで。
ツッコミ随時歓迎です。

6
7
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
6
7