Angular.js と Rails の form_for を良い感じに連携する
最近とある案件で Angular.js を使い始めたのですが。
どうにもRailsと相性がそこまで良くないというか、2つの世界をCoCで繋ぐ決定的なgemなどが出てきていないからか、不便さを感じます。
そんな中で、特に不便に感じたのが、form_for がロクに使えないことでした。
$ rails g model post title:string
みたいなモデルを想定してください。
愚直にやると、Angualr.jsで非同期にPOSTする時のformは、こんな感じになってしまいます。
# slim記法の例 div 'ng-controller'=>'PostCtrl' = form_for(@post, html:{ 'ng-submit'=>'submit()' }) do |f| = text_field :title, 'ng-model' => 'post.title' = button :submit end
うーん、何かこう、いけ好かないですよね。 同じ様な事を何回も書いてるし…
ということで、こんなのを作ってみた。
# /app/helpers/angular_form_builder.rb # http://uzuki05.hateblo.jp/entry/2013/04/05/114356 class AngularFormBuilder < ActionView::Helpers::FormBuilder def ng_text_field(method, options = {}) options[:html] ||= {} @template.text_field(@object_name, method, objectify_options(options[:html].reverse_merge!(ng_options(method)))) end # TODO: text_field 以外の互換メソッドも用意しましょう private def ng_options(method) { 'ng-model' => "#{@object_name}.#{method}", } end end
# /app/helpers/application_helper.rb module ApplicationHelper def ng_form_for(*args, &proc) args << {} unless args.last.is_a?(Hash) options = args.last if args.first.is_a?(Symbol) options.merge!(as: args.shift) end options.reverse_merge!( builder: AngularFormBuilder, format: :json, html: { 'ng-submit' => 'submit()' } ) form_for(*args, &proc) end end
これを使うと、フォーム部分は以下の様に書けます。
# slim記法の例 div 'ng-controller'=>'PostCtrl' = ng_form_for(@post) do |f| = ng_text_field :title = button :submit end
あとは、Angular.jsのControllerをこれに準拠して書いていけば、それなりに柔軟さを保ちつつ、RailsのFormBuilderだけで概ね記述できて良いんじゃないかな、と思います。
ちなみに、CSRF対策については以下のStackoverflowでの議論に準拠すると良いと思います。
Angular.js触って2日目ぐらいなので、もっと良い方法あったらおしえてください!