I'm skipping here the obvious question "why would I need to customize a forms builder?" and get straight to the point.
So you need to customize your forms. Big time. And if the first what comes on your mind are helper methods and partial templates, this article is for you.
And please don't think about monkey patching rails form-builder. Don't behave like a php developer thinking he can use rails. Lord save their poor souls.
There are better way of doing that.
First of all you should realize that in 99.99% of cases you should not change the default rails templates. Meaningly that
<% form_for(@model) do |f| -%>
<%= f.error_messages %>
<p>
<%= f.label :name %>
<%= f.text_field :name %>
</p>
<% end -%>
Despite that it is a simple structure, with a little bit of imagination and css magic you can make it look like anything you need.
Secondly you really should pay a dollar of penalty every time you think about monkey patching. Ruby is not just a monkey patching language it is object oriented too, and rails form builder is a quite well organized structure.
Instead of patching, we will create our own form-builder using advantages of the inheritance and then swap the default builder from the built-in to our own one. This way we can change the logic of the forms building transparently for the templates, you even can have several form-builders which behave differently and swap them depend on the context.
The basic example would look like this. You create another helper module called FormsHelper and save it along with the other helpers in your application
module FormsHelper
def self.included(base)
ActionView::Base.default_form_builder = CustomFormBuilder
end
class CustomFormBuilder < ActionView::Helpers::FormBuilder
# your custom methods go here
end
end
Inside the class you will have access to the form builder context with all its built in methods and variables. For the beginning you should know about the following ones
- @template - the template context, you call this variable if you need to call the basic helpers
- @object - the current model of the form
- @object_name - the string name under which the form knows the model
Okay now lets play with the thing a little. Say the customer says "don't like the <h2> header on the error reports, the one with the number of errors on the form". No problem, we just override the built in method
def error_messages(options={})
super options.reverse_merge(:header_message => nil)
end
Then say you as many other developers usually put your forms in a partial and then depends on the model state change the submit button caption. something like this
form_for(@model) do |f|
f.submit @model.new_record? ? "Create" : "Save"
This is a little bit annoying. Say I'd like just call it <:%= f.submit %> and want the form automatically set the caption. Here is the code
def submit(caption=nil, options={})
super(caption || @object.new_record? ? "Create" : "Save"), options
end
Then I got completely lazy and started to want my form builder to build a block of label + text-field in a single call, like this
form_for(@model) do |f|
f.labeled_text_field :name
# this should build the thing like that
<p>
<%= f.label :name %>
<%= f.text_field :name %>
</p>
# you could do it like this
def labeled_text_field(name, options={})
@template.content_tag(:p, label(name) + text_field(name, options))
end
Then, all the sudden, your customer comes back and says a scary thing "I like this, but I want for this particular controller, there actually was the h2 header saying 'Oh noooo!'. Yes, just for this special case".
No, my friend we are not going to the monkey patchers hell! We just define another form-builder over our own builder, and automatically swap it in the particular controller
class SpecialFormBuilder < CustomFormBuilder
def error_messages(options={})
super options.reverse_merge(:header_message => "Oh noooo!")
end
end
class ParticularController < ApplicationController
before_filter :swap_form_builder
def swap_form_builder
ActionView::Base.default_form_builder = SpecialFormBuilder
end
end
Think you understand the idea. If you do the things in a serious way, lord will love you and grand you a big deal of flexibility and rapidness.
This is pretty much it. Have a good one!
1 comment:
Really good post! Strange noone commented before. I will use this technique to DRY up our views, and also integrate automatic I18n of labels.
Thanks!
Post a Comment