続きはこちら → フォームオブジェクトで fields_for を使う:editの場合
やりたいこと
RailsGuids日本語訳:Action View フォームヘルパーにあるとおり、オブジェクト同士が1対多や1対1の関係になっているときの fields_forヘルパーの使い方はいろいろ分かったのだけど、1対多や1対1の関係になっていない、複数のオブジェクトをまとめて作成したり更新したりするときの fields_forヘルパー の使い方がわからない。
そこで、互いに1対多や1対1の関係になっていないオブジェクトをフォームオブジェクトの要素にした上で、 fields_forヘルパー を使いたい。
参考エントリー
- RailsGuids日本語訳:Action View フォームヘルパー
- Nested forms with ActiveModel::Model objects
- There's an echo in my head:素のオブジェクトをfields_forに使う
関連
想定
フォームオブジェクト BookInputForm を用意し、このオブジェクトには Book オブジェクトが複数含まれる。Bookオブジェクトは、title、author、price、isbnを属性としてもつ。
プロジェクトの用意
% rails new fields_for_demo --skip-bundle % cd fields_for_demo % vi Gemfile
以下をコメントアウト&追加
- gem 'therubyracer', platforms: :ruby
- gem "bootstrap-sass", "~> 3.3.0"
- gem "bootstrap-datepicker-rails"
- gem "bootstrap_form"
% bundle install
Bootstrapを有効にする
bootstrap-sassとbootstrap_formを有効にする。
% mv app/assets/stylesheets/application.css app/assets/stylesheets/application.scss % vi app/assets/stylesheets/application.scss
中身を以下のようにする。
@import "bootstrap-sprockets"; @import "rails_bootstrap_forms"; @import "bootstrap";
Javascriptの方も変更する。
% vi app/assets/javascripts/application.js
中身を以下のようにする。
//= require jquery //= require jquery_ujs //= require turbolinks //= require bootstrap-sprockets //= require_tree .
Bookオブジェクト作成
scaffoldで作る。
% rails g scaffold book title:string author:string price:integer isbn:string % rake db:migrate
フォームオブジェクト作成
% mkdir -p app/forms % touch app/forms/book_input_form.rb % vi app/forms/book_input_form.rb
中身は以下のようにした。books属性にBookオブジェクトを複数個格納する。books_attributes を定義することでフォームオブジェクトでも fields_forを使うことができる(1対多のときの accepts_nested_attributes_for の代わり。ActiveModel::Modelでは、has_manyを指定できないため、こうする必要がある)。
class BookInputForm include ActiveModel::Model attr_accessor :books def books_attributes=(attributes) @books ||= [] attributes.each do | i, book_params| @books.push(Book.new(book_params)) end end def save @books.each do | book | book.save end end end
コントローラー作成
別につくる必要なないのだけど今回は作成した。綺麗じゃないし無駄があると思う。
% touch app/controllers/input_forms_controller.rb % vi app/controllers/input_forms_controller.rb
中身は以下のようにした。
class InputFormsController < ApplicationController def new @book_list = BookInputForm.new(books: [Book.new, Book.new]) # 2つの空の入力欄を用意するため end def create @book_list = BookInputForm.new(book_input_params) @book_list.save redirect_to books_path end private def book_input_params # Rails 4以降のStrong Parameter対応。 params.require(:book_input_form).permit(books_attributes: [:title, :author, :price, :isbn]) end end
config/routes.rb に InputForms を追加しておく
Views
% mkdir app/views/input_forms % touch app/views/input_forms/new.html.erb
中身は以下のとおり。これで、InputFormsController#new で用意した分のBookオブジェクト数だけ、入力欄が表示される。
<%= bootstrap_form_for(@book_list, :url => {:controller => :input_forms, :action => :create}) do | form_data |-%> <%= form_data.fields_for :books do | field_data | -%> <%= field_data.text_field :title %> <%= field_data.text_field :author %> <%= field_data.number_field :price %> <%= field_data.text_field :isbn %> <% end -%> <%= form_data.submit %> <% end -%>
続きはこちら → フォームオブジェクトで fields_for を使う:editの場合