Templated attributes in ActiveRecord

Vandal spraypainting a wall using an 'http://' template
On a recent project, we decided it would be nice to have “templated attributes” for certain fields in a form. A templated attribute has a helpful initial value—kind of like a default value—except that these aren’t valid data or saved in the database. They’re suggestions to the user about the expected formatting or content of a field.

So the game is:

  1. keep these values out of the database
  2. specify these values once in the model, super-duper-DRY
  3. create a user experience that clearly implies that these values are just templates for valid data

Case one: a website attribute

We wanted a website attribute to be prefilled with http:// as a suggestion to the user about proper URL formatting—most users will just enter “example.com” when prompted for a URL. This is to avoid XSS problems with javascript: URIs and the like, and to keep browsers from resolving short URLs to be internal—nothing quite like being sent to http://yoursite.com/www.theirsite.com.

(Aside: there are better ways to handle this specific situation: a :before_validation callback to fix invalid URLs and check for XSS attacks, or the fantastic white_list plugin. But for now I’m interested in the general case.)

This attribute needed certain behavior:

  • When the user hits the form, the field should be pre-filled with http:// if the real value is empty or nil.
  • If the field is left as http://, we should convert it to nil before validation.
  • Client-side: to imply that the initial value is a suggestion, we’ll make the text color gray until the user makes a change. If the user’s only change is to empty the field, we should reset it to http:// and gray again on blur.

Case two: label attributes

There’s another use case, which you’ve seen before: a text field’s initial value is used as a replacement for its label. When the user clicks in the field, the “label” disappears. My example of this is a phone attribute, where we’d like to suggest a standard US area code format. Something like (123) 555-1234.

We don’t want the user to have to delete our dummy numbers and put in their own; it’s too much work. Instead we think that the reminder will help coax the right format out of the user by itself—so this field gets blanked on focus, unlike the website attribute.

You also see this pattern used for content suggestions instead of formatting hints: for example, search fields and login forms which are space-constrained, like the built-in search in Firefox and Safari.

Get on with the plugin, already

OK, OK. So we have two kinds of templated attributes: those with starting values, which are potentially the start of valid data, and labels, which are just helpful, ephemeral reminders.

Check out the goods:

1
2
3
4
class User < ActiveRecord::Base
  templated_attribute :website, :starting_value => 'http://'
  templated_attribute :phone, :label => '(123) 555-1234'
end

Validations work as expected, since unchanged template values get removed in a :before_validation callback. So you can sprinkle on a little :validates_presence_of and :validates_format_of for a really good time.

There’s also some nice, unobtrusive Javascript you can generate to get the behavior I mentioned above. If you’re using form_for, it’s totally automatic. It gets installed when you install the plugin, or you can install and remove manually with these rake tasks:

1
2
rake templated_attribute:install
rake templated_attribute:remove

To turn off the Javascript for a given templated_attribute—say, because the generated stuff doesn’t jibe with your fancy-pantsy, AJAX-validating, Grey Poupon of a form—just throw :templated_javascript => false in the options hash for text_field or text_area. You’ll have to do any styling and event handling by yourself.

I’d like to make this work for fields other than text_field and text_area; the other contenders were file_field, which we can’t do because the Javascript security model doesn’t let us touch its value at runtime, and password_field, which I haven’t done because showing the template value would require dynamically switching the element to a text_field and back (to avoid all those asterisks). That one’s on the list, though.

Plugin resources

Thanks to Azita Mirzaian for the illustration.

Comments

  1. MKAugust 13, 2007 @ 09:43 PM

    Thanks for the Browse the code! Nice artwork,too, throughout.

  2. DanielOctober 18, 2007 @ 09:17 AM

    Hi, nice idea for a plugin - very useful.

    However, when I try to use it I get an error “uninitialized constant x” with x being whatever the name of the object is. This happens on all textfield and textarea functions.

    Any ideas what the problem might be?

  3. Chris KampmeierOctober 18, 2007 @ 10:46 AM

    Daniel: it’s hard to say without seeing some more details. If you want to shoot me an email with the full stack trace, as well as the snippets from your model and views where you’re using it, I’ll be happy to take a look.

  4. Arun January 21, 2008 @ 04:02 AM

    Hi,

    i am using your plugin for my rails app.

    i have formfor insted of formtag

    and this plugin is not work for me in this case can u please guide me where i am wronge

  5. Chris KampmeierJanuary 21, 2008 @ 09:03 PM

    Arun: nice to hear you’re trying out the plugin. Everything should be fully automatic with form_for; are you seeing an error message? What’s not working? I can’t help without some more details.

    On this end, I’m using the plugin in a couple Rails 2.0.2 projects and it’s working as advertised.

  6. ArunJanuary 22, 2008 @ 09:09 AM

    Hi Chris,

    Thanks for your reply! I think the problem was that in our formfor we were being lazy and not providing the object itself. for e.g. formfor(:customer, :url .. ) not form_for(:customer, @customer, :url .. )

    So that fixed the problem! Thanks heaps!

    Cheers, Arun

  7. AdityaJanuary 22, 2008 @ 10:03 AM

    Hi Chris,

    I’m working with Arun and I think there might be a teensy weensy problem that you may give us an idea on.

    We’re using templatedattributed on an associated entity. We’ve got a customer model which has a belongsto association with the address. The Pincode of the address is a templated_attribute.

    Usually the name of pincode field turns out to be customer[temp_address][pincode] with the id as customer_temp_address_pincode. Now when we use the templated_attribute, the name of the field gets changed to address_pincode and and the observer is looking for element customer[temp_address]_pincode. Which basically doesnt work.

    I’m trying to fiddle around in field_with_templating method’s last line and the bit where you generate the javascript part. Seeking your opinion/help!

    Cheers, Aditya

  8. AdityaJanuary 22, 2008 @ 10:03 AM

    Hi Chris,

    I’m working with Arun and I think there might be a teensy weensy problem that you may give us an idea on.

    We’re using templated_attributed on an associated entity. We’ve got a customer model which has a belongsto association with the address. The Pincode of the address is a templatedattribute.

    Usually the name of pincode field turns out to be customer[temp_address][pincode] with the id as customer_temp_address_pincode. Now when we use the templated_attribute, the name of the field gets changed to address_pincode and and the observer is looking for element customer[temp_address]_pincode. Which basically doesnt work.

    I’m trying to fiddle around in field_with_templating method’s last line and the bit where you generate the javascript part. Seeking your opinion/help!

    Cheers, Aditya

  9. AdityaJanuary 22, 2008 @ 10:21 AM

    Hi Chris,

    Sorry about the repeat posts… i think i stuffed up the formatting.

    I’ve now got it working so that it works with sub-models also. See Pastie – lines 16-17 and 27

    http://pastie.caboo.se/141954

    Do let me know if this is okay?

    Thanks heaps!!!

    Cheers, Aditya

  10. Josh MannFebruary 20, 2008 @ 05:14 AM

    Hi Chris-

    Thanks for all the work on this plugin. I have heard good things about it and think it will be just what I need. That being said, when I try to install the plugin using:

    ruby script/plugin install https://svn.shiftcommathree.com/railsplugins/templatedattribute/

    I get:

    Plugin not found: [“https://svn.shiftcommathree.com/railsplugins/templatedattribute/”]

    I have added all repositories via the discover command. I am also able to install other plugins.

    Any idea what may be going on?

    Thanks for any help.

    Regards,

    Josh

  11. Chris KampmeierFebruary 20, 2008 @ 10:52 AM

    Hey Josh,

    I think you’ve run into a bug with the plugin script where it can’t handle non-verified SSL certificates (mine is self-signed). Check out this ticket on the Rails trac for a workaround. (Or, look into Piston for plugin management – it’s what I’m using these days.)

  12. Sean McGilvrayDecember 02, 2008 @ 02:15 PM

    Hello everyone I am new to RoR and I was wanting to add this functionality to my site. I currently have this in the class User < ActiveRecord::Base

    templatedattribute :firstname, :label => ‘Your First Name’

    Then I have this in my partial

    <label for="user_first_name">First Name:</label> <%= form.text_field :first_name, :size => User::FIRST_NAME_SIZE, :maxlength => User::FIRST_NAME_MAX_LENGTH %>

    I have included the css and the js in my layout. It is not automatically adding the label into the field. What am I doing wrong.

    Thank you,

    Sean

Post a comment

Comment
(not published)
(optional)