This example shows how to create a contact form in Rails. Among other aspects, you can see how to perform serverside validations and handle the results. For this example the form values (form.export_values) are passed directly to ActionMailer. A special validation has been set on the section level, to validate the combination of address, postal code and city; if either is given, the others need to be given as well.
User feedback is given by using the flash mechanism and after succesful validation the form is altered accordingly by removing certain elements and freezing the form to an ineditable state. If you look closely at the ActiveForm::Element::Base.define_element_wrapper portion below you will notice how optional/empty fields are removed from the frozen view; which is what the user will see once submitted.
go back to activeform.rubyforge.org
# app/forms/definitions/contact.rb
ActiveForm::Definition::create :contact do |f|
f.section :details do |s|
s.text_element :name, :class => 'required' do |e|
e.validates_as_required :msg => 'enter your name'
e.validates_within_length_range :range => (3..64), :msg => 'your name should be at least 3 characters long'
end
s.text_element :email, :class => 'required' do |e|
e.validates_as_required :msg => 'enter a valid email address'
e.validates_as_email :msg => 'enter a valid email address'
end
s.text_element :phone
s.text_element :address
s.text_element :postal_code
s.text_element :city
s.submit_element :submit, :label => 'Submit', :class => 'fancy_submit'
s.define_validation do |elem|
group = [ elem[:address], elem[:postal_code], elem[:city] ]
partitioned = group.partition {|e| e.blank? }
if !partitioned.first.empty? && !partitioned.last.empty?
partitioned.first.each do |e|
case e.name
when :address then e.errors.add('enter your address', 'required')
when :postal_code then e.errors.add('enter your postal code', 'required')
when :city then e.errors.add('enter your city', 'required')
end
end
end
end
end
f.section :message do |s|
s.textarea_element :message, :class => 'required', :rows => 20, :cols => 40 do |e|
e.define_casting_filter {|value| value.strip }
e.validates_as_required :msg => 'enter your message'
e.validates_within_length_range :range => (10..1600), :msg => 'your message should be at least 10 characters long'
end
end
f.after_validation do |form|
if form.valid?
form.remove_elements_of_type :submit, :button
form.freeze!
end
end
end
# app/controllers/main_controller.rb
end
<!-- extracted from app/views/main/contact.rhtml -->
# environment.rb
ActiveForm::Definition.load_paths << File.join(RAILS_ROOT, 'app', 'forms', 'definitions')
# app/forms/views/default.rb (included in app/forms/definitions/contact.rb)
ActiveForm::Element::Section.define_element_wrapper do |builder, elem, render|
builder.fieldset(:class => 'section') { builder.div(:id => "section-", &render)
}
end
ActiveForm::Element::Base.define_element_wrapper do |builder, elem, render|
return if elem.frozen? && elem.export_value.blank?
builder.div(:id => "elem_", :class => elem.css, :style => elem.style) { elem.render_label(builder)
elem.frozen? ? builder.div(:class => 'text') {render.call(builder) } : render.call(builder)
}
end
# ActiveForm Copyright (c) 2007 atelierfabien - loobmedia
# released under MIT license