[Rails]使用FormBuilder自定義你的表單
什麼是FormBuilder
來自FormBuilder的物件,讓你可以建立跟特定物件相關連的欄位。
這個翻譯有點拗口,讓我們看看實際的例子。
當我們要編輯一個Model的物件時,我們的表單基本上可以寫成這樣。
<%= form_tag("/pages") do %>
<%= label_tag(:user, "User") %>
<%= text_field(:pages, :user) %>
<%= submit_tag("submit") %>
<% end %>
text_field這個helper的第一個參數代表的是我們想要編輯的object,第二個參數則是對應的屬性。這樣的寫法有個潛在的壞處是,如果我們有好幾個欄位要處理,那就要不斷的傳入要編輯的物件名稱。
所以當我們的表單是要處理一個Model的物件時,我們通常會使用form_for
這個helper來處理。以下是程式碼的範例
<%= form_for @page do |f| %>
<%= f.label :user, "User" %>
<%= f.text_field :user %>
<%= f.submit %>
<% end %>
這邊的f
是一個表單構造器(Form Builder)物件(f 變數)。當你傳入一個物件給form_for後(這裡是@page),當我們在block內部使用如<%= f.text_field :user %>
的程式碼時,就會得到如<%= text_field(:pages, :user) %>
的效果。
註:如果在表單中想要得到關聯的object,可以透過f.object得到。
客製化FormBuilder
上面簡單介紹了form_builder的常見用法,但事實上FormBuilder的用處不止於此
假設我們希望客製化我的label,當欄位資料無法通過model驗證時,讓label顯示錯誤訊息並改變顏色。這樣的效果我們可以透過form_builder幫我們客製化,而不用在view裡面使用大量的邏輯。
我們這邊需要驗證的是user這個欄位不能是空白。
class Page < ApplicationRecord
validates :user, presence: true
end
首先在app/helpers下建立一個my_form_builder.rb
檔,並繼承自ActionView::Helpers::FormBuilder
class。
接著override原先的label方法,加入客製化的text訊息。(label的原始碼可以參考)
class MyFormBuilder < ActionView::Helpers::FormBuilder
def label(method, text = nil, optinos = {}, &block)
errors = object.errors[method.to_sym]
if errors
text += " <span class=\"error\" style=\"color:red\">#{errors.first}</span>"
end
super(method, text.html_safe, options, &block)
end
end
然後我們要在form_for
中引入這個客製化的form builder。這邊我們需要使用builder:
來引入MyFormBuilder
<%= form_for @page, builder: MyFormBuilder do |f| %>
<%= f.label :user, "User" %>
<%= f.text_field :user %>
<%= f.submit "送出" %>
<% end %>
這樣當我們user欄位是空白時,我們就可以得到以下的畫面。
這樣的話我們就能夠既使用我們客製化的又不影響本來form_for的效果。
客製化form_for
更進一步,你還可以客製化你的form_for方法,
到到app/helers/application_helper.rb
中,新增一個my_form_for
的方法。在內部引用form_for
,並在options中merge builder。(form_for原始碼)
module ApplicationHelper
def my_form_for(record, options = {}, &proc)
form_for(record, options.merge!({builder: MyFormBuilder}), &proc)
end
end
接著將view中的form_for
改寫成my_form_for
<%= my_form_for @page do |f| %>
<%= f.label :user, "User" %>
<%= f.text_field :user %>
<%= f.submit "送出" %>
<% end %>
可以達成一樣的效果。
@linjiahung, 棒棒哒~~~