tkymtk's blog

Ruby on Rails及びその周辺について調べたこと。Do whatever you want to do at your own pace.

Rails: モデルオブジェクトとフォーム RailsGuides + API

RailsGuidesとRails APIからの書き出し。 個別の例はAPIに詳しくあります。

モデルオブジェクトヘルパー

  • 特にフォームに共通する用途はモデルオブジェクトを編集または作成すること。
  • " *_tag" ヘルパーもその用途に使えるが、煩雑になる
  • そのためRailsはこの用途に特化したヘルパーを用意している。
  • それらには" _tag"接尾子がない。例: text_field text_area
  • 第1引数はインスタンス変数の名前(※インスタンスそのものではない)
  • 第2引数は、オブジェクトのメソッド(普通は属性)の名前
  • Railsはオブジェクトのメソッドの戻り値を、inputのvalueにセットする。
  • そして適切なnameを付ける。
  • 大体は第3引数にオプションハッシュをとる。
  • オプションハッシュはクラス等の指定に使える。例: class: create_input
例:
<%= text_field(:person, :name, class: "create_input") %>

<input id="person_name" name="person[name]" type="text" value="#{@person.name}" class="create_input"/>
#もし@person.nameがHenryなら、value="Henry"

オブジェクトにフォームをバインドする(form_for)

  • 上記のやり方でもやりやすくなったが、完璧にはほど遠い
  • たくさんの属性を持つオブジェクトなら名前を何回も繰り返さなければならない。
  • そこで、どうにかフォームをモデルにバインドしたい。
  • それこそが、まさにform_forがしていること。
例:

articleを扱うコントローラがあると仮定する。

  • app/controllers/articles_controller.rb:
def new
  @article = Article.new
end

form_forを使った、対応するビューは次のようになる。

  • app/views/articles/new.html.erb
<%= 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 %>

↓生成されるHTML

<form accept-charset="UTF-8" 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>
解説:
  • @articleが実際に編集されるオブジェクト
  • オプションハッシュを一つ取る。
    • :urlハッシュでルーティングオプションの指定
    • :htmlハッシュでHTMLオプションの指定
    • :namespaceオプションを用いてフォーム要素のid属性を確実に固有化できる。
    • その際、生成されるHTML idの頭に、アンダースコアとともに付けられる。
  • form_forメソッドはform builderオブジェクトをyieldする。(変数f)
  • フォームコントロールを作成するメソッドは、form builder オブジェクトのfに呼び出される。
より詳しく:
  • 一般的なモデルオブジェクトからフォームを作成するためにform_forに渡せるものは
    • オブジェクトを表す、文字列
    • オブジェクトを表す、シンボル
    • または、オブジェクトそのもの(この場合動作が少し異なる)
  • ブロックでyieldされる変数fは、FormBuilderオブジェクトで、 form_forに渡された第1引数からモデルオブジェクトの情報を取り込む。
  • FormBuilderで定義されたメソッドが、このモデルに基づいたフィールドを生成するのに使われる。
  • 上記の例では、名前がarticleとなり、全ての入力のnameは、article[属性名]となる。
  • 従って、createアクションではparams[:article]:title:bodyといったキーを持つハッシュになる

例:

# 上記の例の場合、次のものが:
<%= f.text_field :title %>
# 次のように展開される
<%= text_field :article, :title %>
# => name="article[:title]"
  • htmlではname属性がarticle[:title]となるinputタグが生成される。
  • サブミット後、入力された値は、コントローラではparams[:article][:title]で使える。
  • FormTagHelperも混ぜて使える。
モデルオブジェクトの場合:

form_forの第1引数が、シンボルや文字列でなく、インスタンス変数の場合

  • フォームの初期値が、インスタンス変数の対応する属性値になる。(例: 編集フォーム)
  • :asを使いPrefixを上書きできる。
    例:
<%= form_for(@person, as: :client) do |f| %>
  ...
<% end %>
# => params[:client]
  • RESTfulリソースだと、さらに簡潔にかける。urlオプション等も自動で設定してくれる。
    例:
## articleを新規作成
# long-style:
form_for(@article, url: articles_path)
# same thing, short-style (record identification gets used):
form_for(@article)
 
## 既存のarticleを編集
# long-style:
form_for(@article, url: article_path(@article), html: {method: "patch"})
# short-style:
form_for(@article)

詳細:

間違いがあれば、ご指摘下さると幸いです。