Ruby gem 'nested_for' 一次增加多筆資料

Adler @ 2014-07-22


今天要分享的是一次多筆增加model資料的方法,使用Ryan Bates的gem "nested_form",適用的情況例如: 1. 購物網站的商家,要為商品一次增加許多商品圖片 2. 待辦事項清單,在清單中要一次增加許多待辦事項

從這裡可以很清楚的看出,如果一次只能新增一張圖片,使用者會超麻煩,也會慢到想殺人;同理,如果待辦事項清單上一次只能增加一個待辦事項,那光讀取頁面的速度,就慢到足以讓人想睡覺了。這時候可以使用nested_form這個gem,在新增附屬的model時可以一次新增許多事項。

示範檔案

我已經做好一個在Heroku上的範例網站,操作時可一邊參考。另外原始碼也在Github上,請一同觀看。

使用方法請看以下說明,這邊假設各位對於nested_attributes還不太熟悉,所以會說明得比較完整一點。

前提

Ruby版本:2.1.2
Rails版本:4.1.0
系統:MAC OSX 10.9.4

準備事項: 1. 安裝Ruby、Rails環境 2. 已建立一個Rails程式,已用scaffold產生出event的model、controller、view 3. event model帶有title、content 4. 另一個model是task,包含eventid、content、due 5. 對於model之間的belongsto和has_many稍有了解

安裝

在Gemfile中安裝nested_form這個gem。

gem "nested_form"

在command line中執行bundle,並且加入要求使用jQuery效果:

//= require jquery_nested_form

建立model的正確關係

首先,確認兩個model之間有建立正確的關係。

event.rb

has_many :tasks

accepts_nested_attributes_for :tasks, 
  :allow_destroy => true, 
  :reject_if => :all_blank

task.rb

belongs_to :event

因為我們要在建立event的同時也建立tasks,所以要寫上accepts_nested_attributes,允許在event的new action時,可以同時建立其中的task資料,就可以不用額外建立task的new action。而後方附帶 :allow_destroy:reject_if的選項,前者代表我們能正確減少表單送出的數量,而後者是確保表單空白時,就不會有資料留下。這邊講起來比較模糊,如果各位跟我當初一樣白目的話,就可以先不寫上後面那兩行,等遇到錯誤時......嗯哼!

修改controller

接下來要在controller中加上允許task的存取,因此我們找到原本scaffold時建立的event_params,在後面加上一些東西,變成:

def event_params
  params.require(:event).permit(:title, :content, :date, :tasks_attributes => [:id, :content, :due, :_destroy])
end

:tasks_attributes把後方[]當中的欄位包起來,其中把:id加入,是為了存取時把所有重複id的task拿掉,以免造成重複資料的建立;而後方的destroy,是為了配合剛剛我們在model中設定的:allow_destroy選項,讓待會兒我們在設定增加和減少欄位時,可以正確的把欄位減少。

修改view:

在scaffold起來的view當中,我們可以修改event當中的form.html.erb,讓new和edit都可以使用。一般我們在建立資料時,撰寫form時都是使用formfor這個helper,而在使用nestedform時,要修改成nestedform_for如下:

app/views/events/_form.html.erb

<%= nested_form_for(@event) do |f| %>
  ......
<% end %>

而在這份表格中,我們要加上:task的表格,讓在建立event時,一同把tasks建立起來。

app/views/events/_form.html.erb

<%= f.fields_for :tasks do |t| %>
  新待辦事項:
  內容:<%= t.text_field :content %>
  截止時間:<%= t.text_field :due %>

  <%= t.link_to_remove "移除此待辦事項" %>
<% end %>

<%= f.link_to_add "新增待辦事項", :tasks %>

上方搭配中文字應該非常清楚了,這個gem最重要的兩個helper,一個是linktoremove,一個是linktoadd,只要按下『新增待辦事項』的按鈕,就可以增加欄位,為這個event增加更多的task;而如果增加太多欄位,則可以利用『移除此待辦事項』的連結,把不需要的欄位移除。

因為我們在model檔案裡面有加上:rejectif => :allblank,所以如果把需要填入資料的地方都留白,就不會儲存,同等於把這筆資料刪除;這個方法可以間接用來移除不想要的欄位。如果剛剛的設定有錯誤,可能就會發生一次增加多個欄位、或是明明刪了欄位卻有儲存等窘境,這都是我過來人的無知和痴愚,請大家盡量避免。

結語

以上就是這個nested_form的基本用法,如果有需要進階用法,例如task表格要建立在event之外,則可以利用data-target的設定,讓兩者有關連。所有更多細節的應用部分,請參考Ryan Bates的github檔案

假如有什麼使用的問題,也歡迎到我建立的範例網站來試用,或是到該網站的Github頁面看詳細的程式碼,謝謝。