Stimulusの習作: Tagifyでのタグ入力(サーバ側)
外観
前回の続きです。
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
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
この記事が気に入ったらサポートをしてみませんか?