Stimulusの習作: Tagifyでのタグ入力(サーバ側)

外観

前回の続きです。

画像1

画像2

View

HTMLの例

<div class="field" data-controller="tagify" data-whitelist="キャラクター,グルメ,...">
<label for="landmark_tag_names">タグ(5つまで)</label>
<input type="text" name="tagify" id="tagify" value="" data-target="tagify.tagify" />
<input data-target="tagify.tagNames" type="hidden" value="駅,公共施設,観光"
  name="landmark[tag_names]" id="landmark_tag_names" />
</div>

app/views/landmarks/_form.html.erb

whitelistに既存のタグ名群を設定する。Tagifyはこれらをドロップダウンに表示する。

Stimulusのコントローラ名を設定する。
=> TagifyController, tagify_controller.js

Stimulusのターゲットに、Tagifyが用いるタグ入力用のテキストフィールドを設定する。
=> tagifyTarget

Stimulusのターゲットに、隠しフィールドを設定する。これをクライアントとサーバ間でのタグ名群のやり取りに用いる。
=> tagNamesTarget
Landmarkモデルにtag_namesメソッドを用意し、カンマ区切りのタグ名を返すようにする。Tagifyはこれをタグ名(群)の初期値とする。

 <% whitelist = Tag.select(:name).order(:name).map(&:name).join(',') %>
 <%= tag.div class: 'field', data: { controller: 'tagify', whitelist: whitelist } do %>
   <%= form.label :tag_names, 'タグ(5つまで)' %>
   <%= text_field_tag :tagify, '', data: { target: 'tagify.tagify' } %>
   <%= form.hidden_field :tag_names, data: { target: 'tagify.tagNames' } %>
 <% end %>

Controller

app/controllers/landmarks_controller.rb

タグ名群をtag_paramsで取得する。

  private
...
   def tag_params
     params.require(:landmark).permit(:tag_names)
   end

新規作成時、Landmarkモデルのadd_tagsメソッドでタグ名群を追加する。

  def create
   @landmark = Landmark.new(landmark_params)
   @landmark.add_tags(tag_params[:tag_names])
...

更新時、関連するタグを削除して、タグ名群を追加する。
全部消すという富豪的アプローチで実装してみました。

  def update
   @landmark.taggings.destroy_all
   @landmark.add_tags(tag_params[:tag_names])
...

Model

app/models/landmark.rb

ViewとControllerで使用するメソッドを実装する。

class Landmark < ApplicationRecord
...
 def tag_names
   tags.map(&:name).join(',')
 end

 def add_tags(tag_names)
   tag_names.split(',').each do |name|
     tag = Tag.find_or_create_by(name: name)
     tags << tag
   end
 end
end

以上です。

付録

Rails ERD

画像3

app/models/landmark.rb

class Landmark < ApplicationRecord
 validates :name, :hiragana, presence: true, uniqueness: true
 validates :hiragana, format: { with: /\A([ぁ-ん]|ー)+\z/,
                                message: 'は、ひらがなを入力してください。' }
 validates :correct, numericality: { greater_than_or_equal_to: 1,
                                     less_than_or_equal_to: 3 }

 has_many :taggings, dependent: :destroy
 has_many :tags, through: :taggings

 def tag_names
   tags.map(&:name).join(',')
 end

 def add_tags(tag_names)
   tag_names.split(',').each do |name|
     tag = Tag.find_or_create_by(name: name)
     tags << tag
   end
 end
end


この記事が気に入ったらサポートをしてみませんか?