linjiahung avatar

[Rails]使用FormBuilder自定義你的表單

linjiahung

Published: 02 Jun 2018 › Updated: 02 Jun 2018[Rails]使用FormBuilder自定義你的表單

[Rails]使用FormBuilder自定義你的表單

alt

什麼是FormBuilder

來自FormBuilder的物件,讓你可以建立跟特定物件相關連的欄位。

這個翻譯有點拗口,讓我們看看實際的例子。

當我們要編輯一個Model的物件時,我們的表單基本上可以寫成這樣。

<%= form_tag("/pages") do %>
  <%= label_tag(:user, "User") %>
  <%= text_field(:pages, :user) %>
  <%= submit_tag("submit") %>
<% end %>

alt

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@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 += " #{errors.first}"
    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欄位是空白時,我們就可以得到以下的畫面。
alt

這樣的話我們就能夠既使用我們客製化的又不影響本來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 %>

可以達成一樣的效果。
alt

參考資料

Action View Form Helpers
ActionView::Helpers::FormBuilder

Leave [Rails]使用FormBuilder自定義你的表單 to:

Written by

Programmer, writer, lifelong learner

Read more #cn posts


Best Posts From linjiahung

We have not curated any of linjiahung's posts yet. But you can encourage our curation team to review posts by visiting them regularly and by referring other readers. Because we give priority to frequently read content.

More Posts From linjiahung