views:

1457

answers:

6

I would like to make editing form fields as user-friendly as possible. For example, for numeric values, I would like the field to be displayed with commas (like number_with_precision).

This is easy enough on the display side, but what about editing? Is there a good way to do this?

I am using the Rails FormBuilder. Upon investigation, I found that it uses InstanceTag, which gets the values for fields by using <attribute>_value_before_type_cast which means overriding <attribute> won't get called.

+7  A: 

The best I have come up with so far is something like this:

<%= f.text_field :my_attribute, :value => number_with_precision(f.object.my_attribute) %>

Or my_attribute could return the formatted value, like this:

def my_attribute
  ApplicationController.helpers.number_with_precision(read_attribute(:my_attribute))
end

But you still have to use :value

<%= f.text_field :my_attribute, :value => f.object.my_attribute %>

This seems like a lot of work.

Luke Francl
+4  A: 

I prefer your first answer, with the formatting being done in the view. However, if you want to perform the formatting in the model, you can use wrapper methods for the getter and setter, and avoid having to use the :value option entirely.

You'd end up with something like this.

def my_attribute_string
  foo_formatter(myattribute)
end

def my_attribute_string=(s)
  # Parse "s" or do whatever you need to with it, then set your real attribute.
end

<%= f.text_field :my_attribute_string %>

Railscasts covered this with a Time object in a text_field in episode #32. The really clever part of this is how they handle validation errors. It's worth watching the episode for that alone.

jdl
+2  A: 

If you want a format to be created or maintained during editing, you will need to add Javascript to implement "masks." Here is a demo.

It was the first hit in these results.

Ian Terrell
A: 

I have done something similar. We format times and lengths using a custom form builder. It makes use of the existing text_field, but wraps it so the value can be customized:

class SuperFormBuilder < ActionView::Helpers::FormBuilder
  include ApplicationHelper
  include FormHelper
  include ActionView::Helpers::TagHelper
  include ActionView::Helpers::FormTagHelper

  def length_field(label,*args)
    scale = 'medium'
    args.each do |v|
      if v.has_key?(:scale)
        scale = v[:scale]
        v.delete(:scale)
      end
    end
  value = length_conversion(@object.send(label.to_sym),scale)
  options = (args.length > 0) ? args.pop : {}
  return has_error(label, text_field_tag(field_name(label),value,*args) + ' ' +  length_unit(scale))
end

private
def field_name(label)
  return @object_name + "[#{label}]"
end

def has_error(label, output)
  return    "<div class='fieldWithErrors'>#{output}</div>" if @object.errors[label]
  return output
end

And it is used like this:

<%= form_for( @section, {:action => 'save', :id => @section.id}, :builder => SuperFormBuilder) do |sf| %> 
   <%= sf.length_field :feed_size_min_w, :size => 3, :scale => 'small'  %>
<% end %>

The end result is a value in the appropriate unit based off their choice on system (Metric, Imperial) and scale IE small = inches or millimeters.

I basically copied the text_field method from the existing form builder, which uses the text_field_tag itself.

There are two gotchas: 1) Knowing the name of the object field and how to access the object to get the value which you want to format. 2) Getting the name right so when the form is submitted it is part of the correct params hash.

The form builder is given a class variable @object. You can get the value of the field using the .send method. In my case I send the label :feed_size_min_w to the @object and get its length back. I then convert it to my desired format, and give it to the text_field_tag.

The name of the field is key to having it end up in the params hash, in my instance the params[:sections] one. I made a little helper function called field_name that takes care of this.

Finally the has_error wraps the field in an error div if there are errors on that label.

Tilendor
+1  A: 

You can use the number_format plugin. By specifying a number_format for an existing numeric attribute inside your model, the attribute will now appear as formatted to Rails in all forms and views. It will also be parsed back from that format (when assigned via forms) prior to insertion into the database. (The plugin also creates purely numeric unformatted_<attribute-name> accessors which can continue to be used for arithmetic, or for direct numerical assignment or retrieval by you for seamless integration.)

class MyModel < ActiveRecord::Base
  # this model has the balance attribute, which we
  #  want to display using formatting in views,
  #  although it is stored as a numeric in the database
  number_format :balance, 
                :precision => 2,
                :delimiter => ',',
                :strip_trailing_zeros => false

  def increment_balance
    unformatted_balance += 10
  end

You can also combine the above with a Javascript solution, which can force the user to maintain the decimal point and thousands separators in place while editing, although this is really not necessary.

Cheers, V.

vladr
A: 

for the classic format like date, I need to use this way ?