はじめに
エンジニア歴一年未満のひよっこの初投稿記事です。
現在業務ではCMSをいじってます。
拙いコードもあるかと思うのでアドバイスやお叱りはコメントでいただけると幸いです。
※実際のコードよりも少し簡略化しているものを書いてます。
環境
- Ruby: 2.3.6
- Rails: 4.2.8
概要
動的フォームを作るgem : cocoonを使ってフォームを作っていた際にlocals
でうまく変数が渡せなかったのですが、公式ドキュメントを使って一応解決できたので備忘録代わりに。
model
以下のように、post
/tag
/category
という3つのモデルがあり、
-
category
-post
/category
-tag
はそれぞれ一対多 -
post
とtag
は多対多
の関係でつながっています。今回はpost
のform内でpost_tagsを生成する必要があり、cocoonを使いました。
class Post < ActiveRecord::Base
belongs_to :category
has_many :post_tags, dependent: :destroy
has_many :tags, through: :post_tags
accepts_nested_attributes_for :post_tags , allow_destroy: true
end
class Tag < ActiveRecord::Base
belongs_to :category
has_many :post_tags, dependent: :destroy
has_many :posts, through: :post_tags
end
class Category < ActiveRecord::Base
has_many :posts
has_many :tags
end
やりたかったこと
上記のアソシエーションを持つため、
『postと同じcategoryを持つtagしか、post_tagsに登録したくない!!』
というのが今回やりたかったことです
エラーとなったform
view
<%= form_for(@post) do |f| %>
<!--中略-->
<div class="form-group">
<%= f.label :post_tags, 'タグ', class: 'control-label' %>
<%= link_to_add_association "タグ追加", f, :post_tags, partial: "parts/tag_fields", data: {association_insertion_node: '#tag-box', association_insertion_method: 'append'} %>
<div id="tag-box">
<%= f.fields_for :post_tags do |d| %>
<%= render 'parts/tag_fields', { f: d, tags: @tags } %>
<% end %>
</div>
</div>
</div>
<%= f.submit "保存", class: 'btn btn-primary' %>
<% end %>
<div class="nested-fields">
<%= f.hidden_field :id %>
<div class="input-group input-group-sm" style="margin-bottom: 5px;">
<div>
<%= f.collection_select :tag_id, tags, :id, :name, {}, class: 'form-control input-sm' %>
</div>
</div>
</div>
controller
class PostsController < ApplicationController
before_action :set_post_and_tags
def edit; end
# 中略
private
def set_post_and_tags
@post = Post.find(params[:post_id])
@tags = @post.category.tags
end
end
以上を実行すると、undefined local variable or method 'tags'
となってしまいます。
「なぜだ!!renderでちゃんとオプション指定しているのに。。」
なにが問題だったのか
オプションの記述でいろいろミスってるかな、、と思い試しましたが解決せず。
基本に立ち返って公式ドキュメントを、、、と、調べたらありました。(始めからそうすべきでした)
render_options : options passed through to the form-builder function (e.g. simple_fields_for, semantic_fields_for or fields_for). If it contains a :locals option containing a hash, that is handed to the partial.
= link_to_add_association 'add something', f, :something,
render_options: {locals: { sherlock: 'Holmes' }}
なるほど、`link_to_add_association`の方にも指定せねばいけないのですね。
## 修正したコード
```html+erb
<%= form_for(@post) do |f| %>
<!--中略-->
<div class="form-group">
<%= f.label :post_tags, 'タグ', class: 'control-label' %>
<%= link_to_add_association "タグ追加", f, :post_tags, partial: "parts/tag_fields", data: {association_insertion_node: '#tag-box', association_insertion_method: 'append'}, , render_options: {locals: { tags: @tags }} %>
<div id="tag-box">
<%= f.fields_for :post_tags do |d| %>
<%= render 'parts/tag_fields', { f: d, tags: @tags } %>
<% end %>
</div>
</div>
</div>
<%= f.submit "保存", class: 'btn btn-primary' %>
<% end %>
少し見辛いですが、
link_to_add_association
の最後にrender_options: {locals: { tags: @tags }}
を付け加えたら解決しました。
参考
- nathanvda/cocoon : https://github.com/nathanvda/cocoon