acts_as_taggable_on で jQuery を使った入力補助を行う

acts_as_taggable_on は便利なプラグインですが、デフォルトでは「tag1,tag2,tag3」のようにタグを半角カンマで区切った入力を行うため、一般のユーザ向けとは言えません。そこで jQuery を使って入力補助を行います。Rails 4.0 + Ruby 2.0 で検証しました。

準備

まずは acts_as_taggable_on を使う準備をします。まずは Gemfile を編集。

gem 'acts-as-taggable-on'

終わったら次の手順でデータベース側の準備をします。

bundle install
bundle exec rails g acts_as_taggable_on:migration
bundle exec rake db:migrate

続いてプラグインを利用するモデルを編集します。今回は Post モデルにしました。

def Post < ActiveRecord::Base
  acts_as_taggable_on
end

また Rails 4 からは Strong Parameter が採用されているため、パラメーターの受取りを許可するようコントローラーに記述します。

  params.require(:post).permit(:tag_list)

ここまでで次のようにフリーテキストによるタグ入力が行えるようになります。

<%= form_for Post.new do |f| %>
  <%= f.text_field :tag_list %>
  <%= f.submit '送信' %>
<% end %>

jQuery による入力補助

上の状態で実際の運用を行うとユーザからの不具合報告が夜中まで飛んで来る事が予想されるので、タグひとつひとつがテキストボックスで入力できるように入力補助を行います。


まずは先ほどのフォームを次のように編集します。

<%= form_for Post.new do |f| %>
  <ul id="post_tags">
    <%= initial_tag_list(@post.tag_list) %>
  </ul>
  <%= add_tag_input %>
  <%= f.hidden_field :tag_list %>
  <%= f.submit '送信' %>
<% end %>

add_tag_input と initial_tag_list というヘルパーメソッドを追加しました。これらを実装します。app/helpers/application_helper.rb を編集。

module ApplicationHelper
  # タグの初期化(更新時に使用)
  def initial_tag_list(tag_list)
    raw tag_list.map{|tag| generate_tag_list(tag)}.join
  end

  def add_tag_input
    link_to '追加', '#', onclick: "add_tag_field(\"#{escape_javascript(generate_tag_list)}\"); return false;"
  end

  def generate_tag_list(tag = nil)
    content = '<li class="tag_list"><input class="tag_input" type="text"'
    content += " value=\"#{tag}\"" if tag
    content += ' />'
    content += remove_tag_input
    content += '</li>'
  end

  def remove_tag_input
    link_to '削除', '#', onclick: "remove_tag_field(this); return false;"
  end
end

内部で使用する generate_tag_list 及び remove_tag_input も実装しました。見慣れない JavaScript の関数を定義したので、次はこれを実装します。app/assets/application.js で直接 JavaScript を記述しますが、慣れている方は CoffeeScript で書いても問題ありません。

// タグの変更を監視
$(document).ready(function() {
  set_tag_watch();
});

$(document).on('page:change', function() {
  set_tag_watch();
});

function set_tag_watch() {
  $(document).on("keyup", ".tag_input", function() {
    change_tag_list();
  });
}

// タグの操作
function add_tag_field(content) {
  $("#post_tags").append(content);
}

function remove_tag_field(link) {
  $(link).closest(".tag_list").remove();
  change_tag_list();
}

function change_tag_list() {
  buf = [];
  $(".tag_input").each(function() {
    str = $(this).val();
    if(str != '') {
      buf.push(str);
    }
  });
  $("#post_tag_list").val(buf.join(','));
}

これで次の図のように入力がテキストフィールドで行えるようになりました。

少し詳しく説明すると、タグの入力欄の内容が変更されるか「削除」が押された時にhidden で指定した tag_list の中身が入れ替わるようになっています。大体のケースではこれで入力が分かりにくいと言われる事は無くなるのではないでしょうか。