ASP.NET MVC LinkButton with HtmlHelper extensions

Download code

We’re really loving using ASP.NET MVC3 for so many reasons, not least the way in which Microsoft seem to have embraced externally developed tools such as jQuery. In Visual Studio the whole experience feels really cohesive and extending it is a breeze as this post will show.

Something we have found missing that seems an unusual oversight is an HtmlHelper extension method for submit buttons. Now I know it’s not much to write:

<input type="submit" value="sign in" />

but it would seem much neater to do this:

@Html.Submit("sign in")

(btw we’re using the razor view engine).

Html.Submit

It’s easy enough to write your own HtmlHelper extension methods for the submit button:

        /// <summary>
        /// Returns an HTML submit button
        /// </summary>
        /// <param name="htmlHelper"></param>
        /// <returns></returns>
        public static MvcHtmlString Submit(this HtmlHelper htmlHelper)
        {
            return htmlHelper.Submit("submit", null);
        }

        /// <summary>
        /// Returns an HTML submit button
        /// </summary>
        /// <param name="htmlHelper"></param>
        /// <param name="value">The text for the button</param>
        /// <returns></returns>
        public static MvcHtmlString Submit(this HtmlHelper htmlHelper, string value)
        {
            return htmlHelper.Submit(value, null);
        }

        /// <summary>
        /// Returns an HTML submit button
        /// </summary>
        /// <param name="htmlHelper"></param>
        /// <param name="value">The text for the button</param>
        /// <param name="htmlAttributes">The html attributes to apply to the button</param>
        /// <returns></returns>
        public static MvcHtmlString Submit(this HtmlHelper htmlHelper, string value, object htmlAttributes)
        {
            // init a tag builder
            TagBuilder tb = new TagBuilder("input");

            // get the attributes
            var attributes = HtmlHelper.AnonymousObjectToHtmlAttributes(htmlAttributes) as IDictionary<string, object>;

            // set the attributes
            tb.MergeAttributes<string, object>(attributes);

            // set the type
            tb.MergeAttribute("type", "submit", true);

            // set the value
            tb.MergeAttribute("value", value);

            // return self closing
            return new MvcHtmlString(tb.ToString(TagRenderMode.SelfClosing));
        }

Html.SubmitImage

What about an input button (aka the old web forms ImageButton control), well that’s pretty straightforward too:

        /// <summary>
        /// Returns an input type=image
        /// </summary>
        /// <param name="htmlHelper"></param>
        /// <param name="imageUrl">The url of the image</param>
        /// <returns></returns>
        public static MvcHtmlString SubmitImage(this HtmlHelper htmlHelper, string imageUrl)
        {
            return htmlHelper.SubmitImage(imageUrl, null);
        }

        /// <summary>
        /// Returns an input type=image
        /// </summary>
        /// <param name="htmlHelper"></param>
        /// <param name="imageUrl">The url of the image</param>
        /// <param name="htmlAttributes">The html attributes to apply to the button</param>
        /// <returns></returns>
        public static MvcHtmlString SubmitImage(this HtmlHelper htmlHelper, string imageUrl, object htmlAttributes)
        {
            // init a tag builder
            TagBuilder tb = new TagBuilder("input");

            // get the attributes
            var attributes = HtmlHelper.AnonymousObjectToHtmlAttributes(htmlAttributes) as IDictionary<string, object>;

            // set the attributes
            tb.MergeAttributes<string, object>(attributes);

            // set the type
            tb.MergeAttribute("type", "image", true);

            // set the image url
            tb.MergeAttribute("src", imageUrl);

            // set the alt attribute
            tb.MergeAttribute("alt", "submit");

            // return self closing
            return new MvcHtmlString(tb.ToString(TagRenderMode.SelfClosing));
        }

And you can now use this like so:

@Html.SubmitImage(Url.Content("~/url/to/image.png"))

Html.SubmitLink

Ok, so far so good, what about the good old LinkButton? I’ve seen several posts around the internet that we should be using ActionLink, but that doesn’t submit the form. So, using the principles of unobtrusive javascript we create the following extension:

        /// <summary>
        /// Returns an A tag with a nested SPAN used to submit a form, requires jquery.link-button.js
        /// </summary>
        /// <param name="htmlHelper"></param>
        /// <returns></returns>
        public static MvcHtmlString SubmitLink(this HtmlHelper htmlHelper)
        {
            return htmlHelper.SubmitLink("submit");
        }

        /// <summary>
        /// Returns an A tag with a nested SPAN used to submit a form, requires jquery.link-button.js
        /// </summary>
        /// <param name="htmlHelper"></param>
        /// <param name="linkText">The text for the link button</param>
        /// <returns></returns>
        public static MvcHtmlString SubmitLink(this HtmlHelper htmlHelper, string linkText)
        {
            return htmlHelper.SubmitLink(linkText, null);
        }

        /// <summary>
        /// Returns an A tag with a nested SPAN used to submit a form, requires jquery.link-button.js
        /// </summary>
        /// <param name="htmlHelper"></param>
        /// <param name="linkText">The text for the link button</param>
        /// <param name="htmlAttributes">The html attributes to apply to the button</param>
        /// <returns></returns>
        public static MvcHtmlString SubmitLink(this HtmlHelper htmlHelper, string linkText, object htmlAttributes)
        {
            // init a tag builder
            TagBuilder tb = new TagBuilder("a");

            // get the attributes
            var attributes = HtmlHelper.AnonymousObjectToHtmlAttributes(htmlAttributes) as IDictionary<string, object>;

            // set the attributes
            tb.MergeAttributes<string, object>(attributes);

            // add the data attribute
            tb.MergeAttribute("data-link-button", "true");

            // add a nothing href
            tb.MergeAttribute("href", "#");

            // set the inner html
            tb.InnerHtml = String.Format("<span>{0}</span>", linkText);

            // return self closing
            return new MvcHtmlString(tb.ToString());
        }

You will notice that the markup rendered by this contains a span nested within the a tag. We implemented the extension method like this to make it easy for us to do style the buttons nicely using the sliding doors technique.

Now, this won’t work without the associated javascript file (jquery.link-button.js):

/// <reference path="jquery-1.5.js" />
(function ($) {
    $("a[data-link-button=true]").live("click", function (e) {
        e.preventDefault();
        var form = $(this).parents('form').first().submit();
        return false;
    });
})(jQuery);

As you can see this relies on jQuery so you’ll need a reference to that too.

Download code

One Response to “ASP.NET MVC LinkButton with HtmlHelper extensions”

  1. [...] This post was mentioned on Twitter by Larry King, Concurrent Dev. Concurrent Dev said: ASP.NET MVC LinkButton with HtmlHelper extensions http://bit.ly/g6tqdq [...]

Leave a Reply