ActiveScaffold + acts_as_taggable + Auto Complete

1 July 2007

I’ve talked before on how to use ActiveScaffold with acts_as_taggable_on_steroids.

The problem with that solution was that, although the checkboxes for every tag are very nice, you couldn’t easily add new tags. For some people, this may be fine, for others, it is not.

Together with a colleague (who wishes not to be named), I found a solution that is quite elegant. Instead of using check boxes, and creating all kinds of subforms in ActiveScaffold, we opted for an auto_completing, comma-separated list of tags.

This article descripes the solution we found. I think you’ll like it very much!

When you try to use acts_as_taggable with ActiveScaffold, you might use something like this in your BooksController.

active_scaffold :books do |config|
   config.columns = [:title, :body, :tags]
   config.list.columns = [:title, :tag_list]
   config.columns[:tags].ui_type = :select
   # ...
end

This is not so useful when you want the flexibility of creating new tags instantly. Therefore, it’s better to use the tag_list:

active_scaffold :books do |config|
   config.columns = [:title, :body, :tag_list]
   config.list.columns = [:title, :tag_list]
   # ...
end

You get a text_field for writing down the tags (comma-separated). The problem of this is that the user has to keep all the tags in mind and is not allowed to make any typos in the tag. To help our users out, I use Rails’ auto_complete feature.

In your BooksController:

auto_complete_for :book, :tag_list

def autocomplete_tag_list
  @all_tags = Tag.find(:all, :order => 'name ASC')

  re = Regexp.new("^#{params[:record][:tag_list]}", "i")
  @tags = @all_tags.find_all do |t|
    t.name.match re
  end

  render :layout => false
end

We can now create the template for the results which are found. In app/views/books/autocomplete_tag_list.rhtml:

<ul class="autocomplete_list">
<% @tags.each do |t| %>
<li class="autocomplete_item"><%= t %></li>
<% end %></ul>

Now comes the difficult part, integration of the auto_complete widget within ActiveScaffold.

ActiveScaffold has the possibility to change the way every attribute is displayed on the create and edit page. I want to change the form for the attribute ‘tag_list’. To do this, I create create a file named app/views/books/_tag_list_form_column.rhtml:

<dl>
<dt>
   <label for="record_tag_list">AutoCompleted Tag List</label>
 </dt>
<dd>
   <%= text_field_tag 'record[tag_list]', @record.tag_list %>
<p class="auto_complete" id="record_tag_list_<%=@record[:id]%>_auto_complete">
        style="{height: 80px;}">

<script type="text/javascript">
    //<![CDATA[
    var record_tag_list_<%= @record[:id].to_s %>_auto_completer =
     new Ajax.Autocompleter(
       \'record[tag_list]\',
       \'record_tag_list_<%=@record[:id]%>_auto_complete\',
       \'/articles/autocomplete_tag_list\', {tokens: \',\'});
    //]]>
   </script>
 </dd>
</dl>

This shows a text field and generates a div that contains the available tags that we can show to the user. To populate the list of tags we use Ajax.Autocompler, which requires three arguments: the id of the text_field; the id of the div where you want to show possible tags to the user; and third, the URL of the action we created before, that returns the proper tags.

The ‘tokens’ part of the last argument indicates that the user can seperate multiple tags with a comma. So, if you’ve entered one tag, added a comma and start typing a new tag, the auto complete feature will only lookup that second tag you’re typing!

That’s it. Just spice things up a bit with some Style, and you’re done. Enjoy!