Monday, April 4, 2011

Making Tags With Rails And RightJS

As the tags widget appeared recently on the RightJS UI list, I'd like to write a simple how-to about tags in case of using RubyOnRails.

You can see the tags widget in action on this demo page, and over here you can find a demo rails application that has the tags feature implemented, along with some other right-rails features.

Let's start

The Initial Model

When you need to add a tags feature in a ruby-on-rails project, a standard model would look kinda like that

class Article < ActiveRecord::Base
has_and_belongs_to_many :tags, :uniq => true
end

class Tag < ActiveRecord::Base
has_and_belongs_to_many :articles, :uniq => true

validates_presence_of :name
validates_uniqueness_of :name
end

As you can see, it's a pretty much straightforward m2m association, articles have many tags and tags have many articles.


Tags To Strings Conversion

The next standard step is usually to add a couple of methods to convert the list of tags into a coma separated string back and forth. The basic reason is that tags are data-base records and it's not much of user-friendly to use integer ids

class Article < ActiveRecord::Base
has_and_belong_to_many :tags, :uniq => true

def tags_string
tags.map(&:name).join(', ')
end

def tags_string=(string)
self.tags = string.split(',').map do |tag|
unless tag.blank?
Tag.find_or_create_by_name(tag.strip)
end
end.compact
end
end

After that you can use this proxy property in forms directly, like that

<%= form_for @article do |f| %>
<p>
<%= f.label :tags_string %>
<%= f.text_field :tags_string %>
</p>
<% end %>

This way the user will always manipulate the tags list as a simple string and your app will automatically create the tag records and associations on fly


Hooking Up RightJS

At this point your rails app will present a kind of a standard and simple case of tags handling. Time to make it look nice and friendly with a bit of javascript.

To hook up RightJS into your Rails application all you need is just add the right-rails gem into your Gemfile

gem 'right-rails'

And run the following code generator

rails g right_rails

Don't worry when you see a tall list of scripts it copies into your public/javascripts directory, right-rails handles most of them automatically, so you won't really need to look inside of those.


Adding The Tags Widget

Ironically, the actual RightJS part of this enterprise is the shortest one. Once you added right-rails into your application, all you need to do is replace 'f.text_field' with 'f.tags_field' in your form

<%= form_for @article do |f| %>
<p>
<%= f.label :tags_string %>
<%= f.tags_field :tags_string %>
</p>
<% end %>

RightRails handles everything else automatically, it generates all necessary html, includes javascript/css modules, it even switches between minified and source builds of javascript libraries depending on the working environment.

More of that, it can automatically use the google hosted rightjs cdn-server for a superfast, shared scripts delivery.


Adding Autocompletion

By default the f.tags_field will make you a pretty looking tags-field, if you want to add the tags autocompletion feature to it, all you need is to specify a list of known tags in it

....
<%= f.tags_field :tags_string, :tags => Tag.all.map(&:name) %>
....


There is also a few additional options you can use with tags widgets, you can find them at the rightjs.org documentation


Conclusion

This is a good example to show how RightJS is build with server-side developers in mind. As you have noticed we didn't write a single line of javascript in our little exercise, not like we couldn't, just we don't have to bother with those routine things every time we need a simple form with a bunch of standard widgets.

More of that, if you take a look into your HTML code, you'll see that RightRails didn't write a single line of script either, all it did is added the data-tags attribute to your input field.

<input data-tags="{tags: ['one','two','three'}" value="one, two"
id="article_tags_string" name="article[tags_string]" />

This way, even if something will go wrong, the user will still see the standard input field with coma separated tags. All your styles and content will remain were it was.


--
Well, this is pretty much the whole story. If you're interested, go check the RightJS UI collection, it has many more useful widgets that you can use in your applications.

No comments: