はじめに
会計ソフトみたいなものを作ってみようと思い、少し着手し始めました。今回はnested_formを試したので、それを紹介したいと思います。
やってみたこと、課題、参照したサイトなどを紹介します。
やってみたこと
次のコマンドを実行しました。
$ rails new sample_app
$ cd sample_app
$ vim Gemfile
$ bundle install
$ rails generate scaffold Entry date:date comment:text
$ rails generate scaffold Debit name:string money:integer entry_id:integer
$ rails generate scaffold Credit name:string money:integer entry_id:integer
$ rake db:migrate
$ vim app/assets/javascripts/application.js
$ vim app/models/entry.rb
$ vim app/models/debit.rb
$ vim app/models/credit.rb
$ vim app/controllers/entries_controller.rb
$ vim app/views/entries/_form.html.erb
編集対象のファイルはrailsコマンドで自動生成されます。編集対象のファイルはvimコマンドを実行したところです。(実際には何度もvimコマンドを実行することはありません。vimのノーマルモードで:e {file}
とタイプすれば、ファイルを編集することができます。)
まずは、Gemfileを編集します。次の行を追加します。
gem 'nested_form'
次に、application.jsを編集します。次の行を追加します。
//= require jquery_nested_form
モデル間の関連は次のように実装します。
class Entry < ActiveRecord::Base
has_many :credits
has_many :debits
accepts_nested_attributes_for :credits, allow_destroy: true
accepts_nested_attributes_for :debits, allow_destroy: true
end
class Debit < ActiveRecord::Base
belongs_to :entry
end
class Credit < ActiveRecord::Base
belongs_to :entry
end
Strong Parametersというのがよく分かっていないのですが、ビュー側でentryが参照しているdebits, credits(に含まれるオブジェクト)の属性にアクセスするためにStrong Parametersを使っているようです。
これらを実装するために、コントローラーに実装を追加します。
class EntriesController < ApplicationController
(中略)
private
# Use callbacks to share common setup or constraints between actions.
def set_entry
@entry = Entry.find(params[:id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def entry_params
params.require(:entry).permit(:date, :comment, debits_attributes: [:name, :money, :entry_id, :_destroy], credits_attributes: [:name, :money, :entry_id, :_destroy])
end
end
ビューは次のように実装しました。
<%= nested_form_for(@entry) do |f| %>
<% if @entry.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(@entry.errors.count, "error") %> prohibited this entry from being saved:</h2>
<ul>
<% @entry.errors.full_messages.each do |message| %>
<li><%= message %></li>
<% end %>
</ul>
</div>
<% end %>
<div class="field">
<%= f.label :date %><br>
<%= f.date_select :date %>
</div>
<div class="field">
<%= f.label :comment %><br>
<%= f.text_area :comment %>
</div>
<div class="field">
<%= f.fields_for :debits do |debit| %>
<%= debit.text_field :name %>
<%= debit.text_field :money %>
<%= debit.link_to_remove "Remove this debit" %>
<% end %>
<p><%= f.link_to_add "Add debit", :debits %></p>
</div>
<div class="field">
<%= f.fields_for :credits do |credit| %>
<%= credit.text_field :name %>
<%= credit.text_field :money %>
<%= credit.link_to_remove "Remove this credit" %>
<% end %>
<p><%= f.link_to_add "Add credit", :credits %></p>
</div>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
ここまでで冒頭のgifのような機能が実現されると思います。
課題
newとeditで同じ_form.html.erbを使っているので、更新しただけで、データが追加されてしまいます。
ほかにもたくさん課題がありそうです。。。
よく分かっていないのですが、debits, creditsのentry_idがなぜか自動で更新されました。どうやって更新しようか悩んでいたのですが、自動で更新されて少し驚きました。なぜなんでしょうか?