Learning Razor–Writing a Once Extension

by rhernandez 15. April 2011 08:47

So, I have been –officially- using Razor since the ASP.NET MVC 3.0 release and I am loving every minute of it, but from time to time I still find myself looking back at Spark features that I wish I had in Razor. One feature that had been staring at me in the face for a while was the use of the ONCE attribute in spark.  The ONCE attribute allows you to specify with a very elegant syntax that something should just be rendered once no matter how many times is specified. (See code snippet below, or whole documentation here at the Spark website).

 

<!--  add jquery if it hasn't been added yet -->
<script once="jquery-1.5.1" 
  type="text/javascript" 
  src="~/content/js/jquery-1.5.1.js"/>

 

There are probably a couple of ways you could make this work in Razor but the easiest way to go about it is to write a Templated Html Helper (More info on the topic by Phil Haack here, and Andrew Nurse here).  Building a Templated HTML Helper allow us to use a syntax almost as sweet as Spark’s (see snippet below).

 

<!--  add jquery if it hasn't been added yet -->
@Html.Once("jquery-1.5.1",
    @<script src='@Url.Content("~/Scripts/jquery-1.5.1.js")' type="text/javascript"></script>
)

 

While the source code is extremely simple it does rely on the use of the HttpContext to establish uniqueness, this implies that it will safely work across Partial and Child Action calls but will not work across AJAX requests.

 

using System;
using System.Web.Mvc;
using System.Web.WebPages;

public static class HtmlHelperExtensions
{
    private static string RAZOR_ONCE_EXTENSION_KEY_FORMAT 
        = "RAZOR_ONCE_EXTENSION_KEY_{0}";

    public static HelperResult Once(this HtmlHelper helper, 
        string onceKey, 
        Func<object, HelperResult> template)
    {
        var contextKey = string
            .Format(RAZOR_ONCE_EXTENSION_KEY_FORMAT, onceKey);
        return new HelperResult(writer =>
        {
            if (helper.ViewContext.HttpContext.Items[contextKey] == null)
                template(null).WriteTo(writer);
            helper.ViewContext.HttpContext.Items[contextKey] = "OUTPUT_RENDERED";
        });
    }
}
Comments are closed

The Author



Roberto Hernández

 

Currently a Managing Consultant for Excella in Arlington, VA. I have been architecting and building software solutions using Microsoft technology for the past 14 years. I was awarded as a Microsoft MVP in C# in 2007,2008 and 2010. I am originally from the Dominican Republic, and the proud father of two beautiful daughters that make my life special. You can track me down on twitter (@hernandezrobert) and on this blog http://overridethis.com.


 

Stack Overflow profile for Roberto Hernandez at Stack Overflow, Q&A for professional and enthusiast programmers

INETA Community Speakers Program

Recent Posts