Code Voyeur
RSS
Data Access Languages MVC ORM About Roadmap Contact Site Map RSS Sample Code Presentations Snippets dll Hell .net Rate My Snippet

ASP.NET MVC Tag Cloud HTML Extensions

This article is essentially a port of a previous Code Voyeur article that demonstrated how to create a reusable tag cloud component in a MonoRail ViewComponent. A brief overview of what tag clouds are, why they are useful and how you might get data to populate one may be found there.

This sample project includes a simple interface for taggable data.

public interface ITaggable {        
    string Tag { get; set; }
    int Weight { get; set; }
    string Controller { get; }
    string Action { get; }
}
The tag and weight properties are essential, as they are used to display tags in the proper font size. The Controller and Action properties are used to build tag links that may vary per ITaggable subclass. The sample project for this article uses a class TaggableArtist.
public class TaggableArtist : ITaggable {

  public string Tag { get; set; }
  public int Weight { get; set; }
 
  public string Controller
  {
      get { return "Artists"; }            
  }

  public string Action
  {
      get { return "Index"; }            
  }
 
}
The extension method is straightforward. It requires an IList of ITaggable along with the number of levels (font sizes) for the tag cloud.
public static string TagCloud(this HtmlHelper html, IList<ITaggable> taggables, int numberOfStyleVariations) {
  ...
}
The body of TagCloud begins with some LINQ method calls to sort the list and find the min and max values. Those values are used to find the boundaries between tag levels.
var sorted = taggables.OrderBy(x => x.Tag);
double min = sorted.Min(x => x.Weight);
double max = sorted.Max(x => x.Weight);
double distribution = (double)((max - min) / numberOfStyleVariations);
The next lines create a StringBuilder instance to store the generated HTML and add HTML for the containing DIV tag.
StringBuilder sb = new StringBuilder();
sb.AppendFormat("<div class=\"tag-cloud\">");
The loop that follows iterates over each tag, determining where in the range of possible tag levels its weight falls. The HtmlHelper extension method RouteLink is used to construct an appropriate link to the action and controller specified by the ITaggable instance. The name of the artist (the tag) is used as the ID value in the route.
foreach (var x in sorted) {
    for (double i = min, j = 1; i < max; i += distribution, j++) {
        if (x.Weight >= i && x.Weight <= i + distribution) {
            string link = html.RouteLink(x.Tag, new { ID = x.Tag.Replace(" ", "-"), Controller = x.Controller, Action = x.Action }, new { @class = "tag" });
            sb.AppendFormat(TAG_HTML_TEMPLATE, j, link);
            break;
        }
    }
}
The TAG_HTML_TEMPLATE is a private constant defined in the HtmlExtensions class.
private const string TAG_HTML_TEMPLATE = "<span class=\"tag-{0}\">{1}</span>\r\n";
Finally, the containing DIV tag is closed and the StringBuilder's content is returned.
sb.Append("</div>");
return sb.ToString();
The Home controller's Index method simply creates a list of taggables and sets them on the Model.
public ActionResult Index() {
    
    IList<ITaggable> artists = new List<ITaggable>()
    {
        new TaggableArtist() { Tag = "Radiohead", Weight = 100 },
        new TaggableArtist() { Tag = "The Shins", Weight = 90 },
        new TaggableArtist() { Tag = "The Killers", Weight = 85 },
        new TaggableArtist() { Tag = "JayMay", Weight = 30 },
        new TaggableArtist() { Tag = "Sara Barellis", Weight = 40 },
        new TaggableArtist() { Tag = "Missy Higgins", Weight = 20 },
        new TaggableArtist() { Tag = "Ben Folds", Weight = 50 },
        new TaggableArtist() { Tag = "Counting Crows", Weight = 30 },
        new TaggableArtist() { Tag = "Franz Ferdinand", Weight = 10},
        new TaggableArtist() { Tag = "Chairlift", Weight = 15 },
        new TaggableArtist() { Tag = "Ok Go", Weight = 30},
        new TaggableArtist() { Tag = "Regina Spektor", Weight = 60},
        new TaggableArtist() { Tag = "Weezer", Weight = 70},
        new TaggableArtist() { Tag = "Nine Inch Nails", Weight = 80},
        new TaggableArtist() { Tag = "Third Eye Blind", Weight = 65},
        new TaggableArtist() { Tag = "Led Zeppelin", Weight = 80},
        new TaggableArtist() { Tag = "The Beatles", Weight = 40},
        new TaggableArtist() { Tag = "Elvis Costello", Weight = 90},
        new TaggableArtist() { Tag = "Audioslave", Weight = 30}
    };

    ViewData.Model = artists;
    return View();
}
The call to the new extension method is as follows:
<%= Html.TagCloud(ViewData.Model, 5) %>

Download Sample Project

References

Source of the markup for the tagcloud
Article Posted: Wednesday, February 18, 2009

Comments

Posted by coolguy97 on 8/20/2011 7:19:45 AM
Code doesn't work when only 1 tag is returned.

Leave a Comment

Shout it

Contact Code Voyeur about this article.