Rails中的Helper方法

weixin_34082695發表於2016-04-14

tag類的Helper

form_tag

<%= form_tag("/search", method: "get") do %>
  <%= label_tag(:q, "Search for:") %>
  <%= text_field_tag(:q) %>
  <%= submit_tag("Search") %>
<% end %>
#生成HTML如下
<form accept-charset="UTF-8" action="/search" method="get">
  <label for="q">Search for:</label>
  <input id="q" name="q" type="text" />
  <input name="commit" type="submit" value="Search" />
</form>
#生成的表單的每個input屬性都有ID屬性,其值和name屬性的值是一樣的

form_tag({controller: "people", action: "search"}, method: "get", class: "nifty_form")
#第二個hash引數為html屬性
<form accept-charset="UTF-8" action="/people/search" method="get" class="nifty_form">

核取方塊

<%= check_box_tag(:pet_dog, "12") %>

<input id="pet_dog" name="pet_dog" type="checkbox" value="12" />

單選框

<%= radio_button_tag(:age, "child") %>

<input id="age_child" name="age" type="radio" value="child" />

處理模型物件

#如果控制器定義了@person例項變數,有屬性name ="Henry"
<%= text_field(:person, :name) %>
#生成如下
<input id="person_name" name="person[name]" type="text" value="Hery" />
#params獲取的值為
{'person' => {'name' => 'Henry'}}
#可以使用params[:person][:name]獲取提交的值
#如果@person例項是資料庫表nurse_aid_person,那麼產生的程式碼如下
<input id="person_name" name="nurse_aid_person[name]" type="text" value="Hery" />

Hash

#Hash可以隨意巢狀
<input id="person_address_city" name="person[address][city]" type="text" 
  value="New York"/>
#獲取的params為
{'person'=>{'address'=>{'city'=>'New York'}}}
params[:person][:address][:city]

陣列

#一般情況下Rails回忽略重複的引數名
<input name="person[phone_number][]" type="text"/>
<input name="person[phone_number][]" type="text"/>
<input name="person[phone_number][]" type="text"/>
#params[:person][:phone_number]是一個陣列

Hash和陣列結合

<input name="addresses[][line1]" type="text"/>
<input name="addresses[][line2]" type="text"/>
<input name="addresses[][city]" type="text"/>
#params[:addresses]值是一個由Hash組成的陣列

form_for表單繫結物件

<%= form_for @article, url: {action: "create"}, html: {class: "nifty_form"} do |f| %>
  <%= f.text_field :title %>
  <%= f.text_area :body, size: "60x12" %>
  <%= f.submit "Create" %>
<% end %>

<form action="/articles/create" method="post" class="nifty_form">
  <input id="article_title" name="article[title]" type="text" />
  <textarea id="article_body" name="article[body]" cols="60" rows="12"></textarea>
  <input name="commit" type="submit" value="Create">
</form>

fields_for方法

#同一表單中編輯多個模型物件時經常使用fields_for方法。
#eg、有個Person模型,和ContactDetail模型關聯
<%= form_for @person, url: {action: "create"} do |person_form| %>
  <%= person_form.text_field :name %>
  <%= fields_for @person.contact_detail do |contact_details_form| %>
    <%= contact_details_form.text_field :phone_number %>
  <% end %>
<% end %>

#生成的HTML程式碼如下
<form action="/people/create" class="new_person" id="new_person" method="post">
  <input id="person_name" name="person[name]" type="text" />
  <input id="contact_detail_phone_number" name="contact_detail[phone_number]" type="text" />
</form>

名稱空間

#admin名稱空間
form_for [:admin, @article] #admin_article_path(@article)

form_for[:admin, :management, @article]

檔案處理

上傳檔案

#params[:picture]
<%= form_tag({action: :upload}, multipart: true) do %>
  <%= file_field_tag 'picture' %>
<% end %>

#params[:person][:picture]
<%= form_for @person do |f| %>
  <%= f.file_field :picture %>
<% end %>

處理上傳

#可以使用CarrierWave和Paperclip等第三方庫
def upload
  upload_io = params[:person][:picture]
  File.open(Rails.root.join('public', uploads, upload_io.original_filename), 'wb') do |file|
    file.write(uploaded_io.read)
  end
end

使用Ajax上傳檔案

<%= form_for @person, builder: LabellingFormBuilder do |f| %>
  <%= f.text_field :first_name %>
<% end %>
#定製表單構造器
class LabellingFormBuilder < ActionView::Helpers::FormBuilder
  def text_field(attribute, options=[])
    label(attribute) + super
  end
end

外部資源

#如果不需要access_token的話,可以關閉,如果需要,設定authenticity_token的值
<%= form_tag 'http://www.baidu.com', authenticity_token:  false do %>
...
<% end %>

複雜的表單

class Person < ActiveRecord::Base
  has_many :addresses
  accepts_nested_attributes_for :addresses, allow_destroy: true,
    reject_if: lambda{|attributes| attributes['kind'].blank?}
#自動為Person物件建立address_attributes=方法,用於建立、更新、刪除地址操作
end

class Address < ActiveRecord::Base
  belongs_to :person
end

巢狀表單

<%= form_for @person do |f| %>
  Addresses:
  <ul>
    <%= fields_for @person.addresses do |addresses_form| %>
      <li>
        <%= addresses_form.label :kind %>
        <%= addresses_form.text_field :kind %>

        <%= addresses_form.label :street %>
        <%= addresses_form.text_field :street %>
      </li>
    <% end %>
  </ul>
<% end %>

如果關聯支援巢狀屬性,fields_for方法會為關聯中的每個元素執行一遍程式碼塊,如果沒有Address,就不執行,一般做法是在控制器中構建一個或者多個空的子屬性。這樣至少會有一組欄位顯示出來。

def new
  @person = Person.new
  2.times { @person.addresses.build }
end

如果使用者提交了兩個地址,提交的引數如下

{
  'person' => {
    'name' => 'John Doe',
    'addresses_attributes' => {
      '0' => {
        'kind' => 'Home',
        'street' => '221b Baker Street'
      },
      '1' => {
        'kind' => 'Office',
        'street' => '31 Spooner Street'
      }
    }
  }
}

控制器端

def create
  @person = Person.new(person_params)
end

private
  def person_params
    params.require(:person).permit(:name, addresses_attributes:[:id, :kind, :street])
  end

刪除屬性

#表單
<%= form_for @person do |f| %>
  Addresses:
  <ul>
    <%= f.fields_for :addresses do |addresses_form| %>
      <li>
  #如果屬性組成的hash中包含_destroy鍵,且值為1或true,就會刪除物件
        <%= addresses_form.check_box :_destroy%>
        <%= addresses_form.label :kind %>
        <%= addresses_form.text_field :kind %>
        ...
      </li>
    <% end %>
  </ul>
<% end %>

修改控制器中的引數白名單

def person_params
  params.require(:person).
    permit(:name, addresses_attributes: [:id, :kind, :street, :_destroy])
end

相關文章