Using Formtastic to Cleanly Create Nice Looking Forms in Rails
Forms can easily get cluttered when you’re dealing with a lot of form fields… er, ERB tags. I’ve written about extending Rails form builders, which certainly goes along way to shrinking your views where forms are used. The plugin Formtastic is even better, as it lets you skirt maintaining your own library in favor of a very, elegant DSL.
For a great overview of the plugin and implementation details, check out Ryan Bate’s Railscast. There are a couple episodes, but be sure to catch the first one, linked above.
I usually install the plugin as a gem
# config/environment.rb #... config.gem "formtastic"
and then run a generator to create the stylesheets for me, making forms look nice and neat.
./script/generate formtastic
One gotcha, is that in order for the css to render correctly you need to add this
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
in the head of your layout file.
You define forms usingĀ “semantic_form_for” like so…
<% semantic_form_for @user do |f| %>
<% f.inputs do %>
<%= f.input :login, :label => "Username", :hint => "Something short because it's in the url"%>
<% end %>
<%= f.buttons %>
<% end %>
As per usual, there are a myriad of configuration options which can be overridden if necessary. Consult the documentation at Justin French’s Github account for specifics.
Ruby on Rails: builders custom tag builder extending forms label Rails template text field
by bseanvt
1 comment
Extending Rails Form Builders
Extending forms in Rails is simple and will greatly reduce the amount of code in your views. This example is taken right from the Agile Web Development book on Rails(2.1.*) with one minor tweak. I want to pass a label argument along with the field name so that I can display a more human friendly string to represent the form field.
# RAILS_ROOT/app/helpers/custom_tag_builder.rb
class CustomTagBuilder < ActionView::Helpers::FormBuilder
def self.create_tagged_field(method_name)
define_method(method_name) do |label, *args|
label_name = args.first.blank? ? label : args.first[:label] # my change
@template.content_tag("p",
@template.content_tag("label",
label_name.to_s.humanize, :for => "#{@object_name}_#{label}") +" <br/> "+ super)
end
end
field_helpers.each do |name|
create_tagged_field(name)
end
end
You can then use this in your views
form_for @your_model, :builder => CustomTagBuilder do |f| f.text_field :fullname f.text_field :email, :label => "Email (will not be published)"
My change tests for the presence of a label argument otherwise using the name of the form field/model attribute. In this case the fullname attribute will be outputted as “Fullname” while “Email (will not be published)” as the label for the email text field.
Ruby on Rails: attr_accessor forms getters Rails setters troubleshooting validations virtual attributes
by bseanvt
leave a comment
Trouble Using Attr_Accessor in Rails Models and Forms
You might use the attr_accessible method to create getters and setters for a class that has attributes which don’t map directly to corresponding fields in a database. For example let’s take the scenario where you are processing a credit card transaction. You don’t want to save the credit card details, such as card number and verification value etc, however you still want to use these attributes in a form and you want to perform validations on them.
class CreditCardPurchase < ActiveRecord::Base attr_accessor :number, :cvv end
The only probem that I’ve run into is using the attr_accessor on datetime select form fields. Rails won’t be able to determine the “klass” and will spit out a nasty error. Looks there is some discussion around this bug. I’ve got another post about this topic http://seanbehan.com/ruby-on-rails/problem-slash-bug-in-rails-with-attr_accessor-and-datetime-select-fields/ but unfortunately not a whole lot of resultion :(
Nested Attributes in a Form for Has_One Model Association in Rails
Just for reference…
class Member < ActiveRecord::Base
has_one :member_profile
accepts_nested_attributes_for :member_profile
end
<p>
<% form_for @member do |f| -%>
<%= f.label :fullname %> <%= f.text_field :fullname %>
<% f.fields_for :member_profile, @member.member_profile do |member_profile|%>
<p><%= member_profile.label :about %><%= member_profile.text_field :about %></p>
<p><%= member_profile.label :favorite_color %><%= member_profile.text_field :favorite_color %></p>
<% end %>
</p>
Using Prototype to Access Form Data
Prototype has a powerful API for accessing and manipulating the Document Object Model, A.K.A the DOM. The following code will let you interact with a simple web form.
Suppose we have a form that contains hidden/or locked inputs and they need to be updated dynamically. If the user changes a select field or if a checkbox is de/selected, other values in the form need updating. This could be required when products have options and associated price points. If you want to offer the user the option to switch options without refreshing or navigation this is a simple and effective approach.
… Sample form…
<input type="hidden" name="option_plan_id" id="option_plan_id" value="1"><br/> <input type="hidden" name="amount" value="150" id="amount"> <select name="select-plan" id="select-plan"> <option value="1, 150"> USD 150 - 1 person </option> <option value="2, 200"> USD 200 - 2 people </option> </select>
In this example I want to update the values on the amount and option_plan_id input fields. The first thing I need to make sure of is that the page has fully loaded. I’ll make sure that my code executes in this context by placing it inside the document.observe method. I’ll then set up an observer on my select field and if it changes, make assignments. Now, in this example, I just saved the plan id and amount information as a string. In my javascript code I split apart the string and then make the assignments. There are other, probably better, ways of doing this. For instance you could store serialized data/json, and then work with it inside your event. This seems simple enough, however, you’ll have to pay attention in the future, if your storage format changes! Here is the sample Prototype javascript code
<script type="text/javascript" charset="utf-8">
document.observe("dom:loaded", function(){ //when dom is fully loaded do -
//update fields when option is selected
Event.observe('select-plan', 'change', function(event){
var data = this.getValue();
var plan = data.split(",").first(); //breaks apart by comma
var amount = data.split(",").last();
$('plan_option_id').setAttribute('value', plan);
$('amount').setAttribute('value', amount);
});
});
</script>
Thats it for now!


