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.
[...] 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 [...]