Thursday, August 13, 2009

Managing Tags in Rails

In this article I'm going to document a small piece of the administration component of the Ruby Rails Review that I wrote. On Ruby Rails Review we kept a collection of interesting articles regarding Ruby on Rails, and these articles were tagged with various tags so that if you went to browse the articles you could filter them by the various tags. Of course, there's no reason to implement that whole tagging system ourselves since there are plug-ins for that. To make the articles taggable we used the acts_as_taggable_on plug-in. This article just gives a method for easily editing the tags for a given article which works much like the Blogger labeling I used when writing this article. For Ruby Rails Review, the article editor form is shown below, though I've dimmed out everything but the tagging sections: As you can see, there is a text field where you can type in tags in a comma separated list, as well a list of all currently defined tags. Selected tags are shown in blue and underlined to give a visual indication that they are selected, but all tags are clickable to either select or deselect them. As tags from the list at the bottom are selected or deselected, they are added or removed from the text field. If you enter tags in the text field they are either selected or added (or deselected if you remove a tag) from the tag list below. So, how do we do this? First, the form code looks like this:

<% form_for @article_detail, :url => { :action => "update", :id => @article_detail.article_id } do |f| %>
   ...
   <%= f.text_field :tag_list, :onchange => "updateTagList(this.value);" %>
   ...
<% end %>
<div id="tags" style="margin-top:30px;">
<% @tags.each do |tag|%>
<div class="tag<%= ' selectedTag' if @article_detail.tag_list.index(tag.name) %>" onclick="selectTag(this);"><%= tag.name %></div>
<% end %>
</div>
You'll also need a little bit of CSS added to make the selected tags stand out (added to your main.css). I happened to make them blue and underlined, but you can make them highlighted or whatever you please:
.selectedTag {
      color: #247CD4;
      text-decoration: underline;
}
Finally, add the necessary JavaScript in either your application.js or a separate JavaScript file of you choosing:
function selectTag(tagElem) {
    tagElem.toggleClassName('selectedTag');
    var selected_tags = buildTagList();
    $('article_detail_tag_list').value = selected_tags;
    $('article_detail_tag_list').focus();
};

function updateTagList(selected_tags) {
    selected_tags = selected_tags.split(',');
    var tags = $H();
    $$('.tag').each(function(tag) { 
        tags.set(tag.innerHTML, tag);
        tag.removeClassName("selectedTag");
    });
    selected_tags.each(function(tagName) {
        tagName = tagName.strip();
        var tag = tags.get(tagName);
        if (tag) {
            tag.addClassName("selectedTag");
        } else {
            $('tags').insert('<div class="tag selectedTag" onclick="selectTag(this);">'+tagName+'</div>');
        }
    });
};

function buildTagList(tag) {
    var selected_tags = [];
    $$('.selectedTag').each(function(tagElem) {
        selected_tags.push(tagElem.innerHTML);
    });
    if (tag) {
        if (selected_tags.indexOf(tag) === -1) {
            selected_tags.push(tag);
        } else {
            selected_tags = selected_tags.without(tag);
        }
    }
    selected_tags = selected_tags.join(',');
    return selected_tags;
}
That's it! Now you can easily maintain the tags on your taggable items.

2 comments:

Wojtek said...

Thanks! That's a useful piece of code!

All shore said...

Hi

Great information in this post and I think this article just gives a method for easily editing the tags for a given article which works much like the Blogger labeling I used when writing this article.

James Parker…..
Outsourcing Services