Creating reusable HTML components in ASP.NET MVC using Razor

Posted on Friday, 09 May 2014

Whilst working on a side project I started to notice I was using a lot of the same HTML to create floating boxes. Whilst these boxes looked the same, the content of them changed quite dramatically; for instance, some of the content boxes contained forms, others contained straight text, like so:

<div class="panel">
         <div class=panel-inner">
             <h2 class="panel-title">Panel Title</h2>
             <div class="panel-content">
                 /* Can I pass content to be rendered in here here? */
             </div>
         </div>
     </div>
</div>

As my side project progressed and grew, I found myself making more and more modifications to these HTML components so I started to look how I can encapsulate the component for greater flexibility and extensibility as my project progressed.

The solution I ended up with was creating a HTML extension modelled off of the way the the Html.BeginForm() extension works, by writing directly to the view's context and then returning an instance of IDisposable, with the call to disposing of the context writing the closing HTML statements of my component to the view context - essentially wrapping the contents passed into the HTML extension in my HTML component.

Below is an example of what the code looks like:

namespace System.Web.Mvc
{
    public static class HtmlHelperExtensions
    {
        public static HtmlPanelComponent PanelComponent(this HtmlHelper html, string title)
        {
            html.ViewContext.Writer.Write(
            "<div class=\"panel\">" +
            "<div class=\"panel-inner\">" +
            "<h2 class=\"panel-title\">" + title + "</h2>" +
            "<div class=\"panel-content\">"
            );

            return new HtmlPanelComponent(html.ViewContext);
        }
    }

    public class HtmlPanelComponent : IDisposable
    {
        private readonly ViewContext _viewContext;
        public HtmlPanelComponent(ViewContext viewContext)
        {
            _viewContext = viewContext;
        }
        public void Dispose()
        {
            _viewContext.Writer.Write(
            "</div>" +
            "</div>" +
            "</div>"
            );
        }
    }
}

Using this new HTML extension I'm now able to reuse my HTML component and fill the panel's content in with whatever I please, like so:

@using (Html.PanelComponent("Panel Title"))
{
    <p>Welcome back, please select from the following options</p>
    <a href="#">Profile</a>
    <a href="#">My Defails</a>
}
Back