🫖

チェックボックスで複数タグ ~複数タグを登録~

2023/04/27に公開1

キーワード検索以外に、タグ検索ができたら便利です。
タグは、ユーザーが投稿する際にフリーワードで登録することもできますが、私が作成したいサイトでは必要なタグが限られているため、管理者が用意したタグをチェックボックスで選択するようにしました。

  • タグの役割
    ・投稿詳細画面で、その投稿に関して知りたい情報をパッと知ることができる
    ・検索により、知りたい情報を絞り込む

ここからの手順

① 管理者側でタグを用意する
② ユーザー側の投稿フォームでタグ登録項目を追加する
③ チェックボックスによるタグ検索フォームを実装する

今回はまず ①と② です。


① 管理者側でタグを用意する

モデル

Postモデル 
Tagモデル
 (必要なカラム) name:string
PostTagモデル
 (必要なカラム) post_id:integer tag_id:integer

アソシエーション

post.rb
has_many :post_tags, dependent: :destroy
has_many :tags, through: :post_tag
tag.rb
has_many :post_tags, dependent: :destroy
has_many :posts, through: :post_tag
post_tag.rb
belongs_to :post
belongs_to :tag

タグを用意する(管理者)

コントローラ

tagsコントローラにアクション定義します。
タグを作成するのは管理者なので、admin側にファイルを作成します。

admin/tags_controller.rb
class Admin::TagsController < ApplicationController
  before_action :authenticate_admin!
  
  def create
    @tag = Tag.new(tag_params)
    @tags = Tag.all
    @tag.save
  end

  def index
    @tags = Tag.all
    @tag = Tag.new
  end

  def destroy
    @tags = Tag.all
    @tag = Tag.find(params[:id])
    @tag.destroy
  end

  private
  def tag_params
    params.require(:tag).permit(:name)
  end

end

タグの追加、削除は非同期通信化しているので、createアクション、destroyアクションのredirect to...は削除しています。

ビュー

管理者側でタグを作成できるように投稿フォームを作成します。
以下画像のように、タグ一覧画面(posts/index)で、一覧表示と投稿フォームを一緒に表示します。

コードは次のように記述しました。

views/admin/tags/index.html.erb
<div class="align-center">
  <div class="row m-3"><h5>登録済み一覧</h5></div>
  <div class="tag-index">
    <%= render 'tag_index', tags: @tags %>
  </div>
</div>
<div class="row">
  <div class="col-md-11 mx-auto">
    <h5 class="m-3">追加登録</h5>
    <div class="tag-form">
      <%= render 'tag_form', tag: @tag %>
    </div>
  </div>
</div>

タグ一覧と投稿フォームは部分テンプレートです。

_tag_index.html.erb
<div class="item-wrap row mb-5">
<% tags.each do |tag| %>
  <div class="item shadow-sm col-md-4 p-3 my-3">
    <%= tag.name %>
    <%= link_to admin_tag_path(tag.id), method: :delete, remote: true, style: 'color: red;', 'data-confirm'=>'このタグを削除します' do %>
      <i class="fas fa-trash-alt"></i>
    <% end %>
  </div>
<% end %>
</div>
_tag_form.html.erb
<%= form_with model: tag, method: :post, url: admin_tags_path, local: false, html: {class: 'p-5 shadow-lg'} do |f| %>
  <div class="form-group">
    <%= f.label :name, 'タグ名', class: 'col-md-2' %>
    <%= f.text_field :name, autofocus: true, autocomplete: 'off', class: 'col-md-6', id: "name_text_field" %>
    <%= f.submit '追加', class: 'btn btn-sm btn-outline-info col-md-2 offset-1' %>
  </div>
<% end %>

非同期通信化

index.html.erb
<div class="tag-form">
  <%= render 'tag_form', tag: @tag %>
</div>
<div class="tag-index">
  <%= render 'tag_index', tags: @tags %>
</div>
  • タグをフォームから登録する際には、
    投稿フォームだけでなく、一覧表示にも非同期で反映される必要があるため、全部で3行記述します。
  1. 非同期でタグ追加
  2. 非同期で一覧表示に反映
  3. 投稿(タグ登録)後、フォームを空欄にする
create.js.erb
$ (".tag-form").html("<%= j(render 'tag_form', tag: @tag) %>");
$ (".tag-index").html("<%= j(render 'tag_index', tags: @tags) %>");
$ ("#name_text_field").val("");
  • タグを削除する際は一覧表示から行い、登録フォームは関係ありません。
    なので、この1行のみでOKです。
destroy.js.erb
$ (".tag-index").html("<%= j(render 'tag_index', tags: @tags) %>");

ここまでで、管理者側でタグを登録することができるようになりました。
この後タグを増やしたい場合には、管理者側のタグ一覧画面のフォームで増やすことができます。
(削除も可能です)

② ユーザー側の投稿フォームでタグ登録項目を追加する

コントローラ

public/posts_controller.rb
def post_params
  params.require(:post).permit(............, tag_ids: [])
end

このように、postsコントローラでtag_ids: [] を追加することで、データベースに複数のタグを保存できます。

ビュー

新規投稿画面、投稿編集画面にタグ選択フォームを実装します。
(以下ではタグ以外の項目に関しては省略しています)

posts/new.html.erb
<%= form_with model:@post do |f| %>
      :
      :
    <div class="form-group row">
      <%= f.label :tag_ids, 'タグ', class: 'col-md-4' %>
      <div class="col-md-8 border d-flex flex-row flex-wrap">
        <%= f.collection_check_boxes :tag_ids, @tags, :id, :name do |b| %>
          <div class="mx-2 mt-1">
            <%= b.check_box %>
            <%= b.label %>
          </div>
        <% end %>
      </div>
    </div>
      :
      :
  <%= f.submit '送信' %>
<% end %>

collection_check_boxes により、複数タグを選択するチェックボックスを表示します。
引数が4つあります。まとめると、データベースに保存されているtagテーブルのレコードを取得してチェックボックスとして表示する ということになります。

⑴ :tag_ids  ⑴〜⑷で取得したデータを:tag_idsに格納する
⑵ @tags   @tags=Tag.all(全てのタグのデータ)という塊を作る
⑶ :id    @tags=Tag.allの中から:idデータを探す
⑷ :name   Tagモデルの:id(引数⑶)から紐づく:nameカラムに該当するデータ取得

<%= f.collection_check_boxes :tag_ids, @tags, :id, :name %>
だけだと、チェックボックスと対応するタグ名がセットにならず、間で改行されてしまうことがあります。なので、以下のようにコードを記述します。

<%= f.collection_check_boxes :tag_ids, @tags, :id, :name do |b| %>
  <div class="mx-2 mt-1">
    <%= b.check_box %>
    <%= b.label %>
  </div>
<% end %>

デベロッパーツールを確認すると、チェックボックス=check_box、タグ名=labelとなっていたので、その2つを上記のように記述しました。この2つを<div>で囲うことでチェックボックスとタグ名がセットとなるため、間で改行されることはなくなります。

投稿詳細画面でもタグを表示する

投稿にタグが含まれたので、投稿詳細画面では登録したタグを表示します。

posts/show.html.erb
<div class="row my-3">
  <div class="col-md-3">タグ:</div>
  <div class="col-md-9"><%= post.tags.pluck(:name).join(' / ') %></div>
</div>

「タグ:」という文字に続き、登録したタグ名が表示されます。
pluck(:name).join(' / ')により、タグ名は/で区切られています。

タグ検索を実装するための手順の2つを記述しました。
 ① 管理者側でタグを用意する
 ② ユーザー側の投稿フォームでタグ登録項目を追加する

長くなってしまったので、ここで区切ります。

次回、
 ③ チェックボックスによるタグ検索フォームを実装する
についてまとめます。

以上です。

Discussion

AirichanAirichan

このようなタグ検索、自分はつけなかったから参考につけて見たいと思う!!!!