# Monday, June 15, 2009

ASP.NET MVC Strongly-Typed ActionLink with Images

Today I had the desire to change some of the ActionLinks we have been using from boring text to exciting images (which meant using my image collection as I have no talent whatsoever in drawing anything). Unfortunately I was unable to locate a respective method on the existing HtmlHelper.

I am a big fan of the strongly-typed variety of these methods, which are, as of today, only available in the MVC Futures (available at CodePlex).

In my hunt for a efficient (lazy) copy-and-paste solution I came across a thread on Stack Overflow with a comment by eu-ge-ne. His code does a nice job of getting an image produced, but did not get me the desired strongly-typed implementation I was seeking.

His version was this (with a minor fix and my personal dislike of var replaced):

public static string ActionLinkWithImage(this HtmlHelper html, string imgSrc, string actionName)
{
    UrlHelper urlHelper = new UrlHelper(html.ViewContext.RequestContext); 
    string imgUrl = urlHelper.Content(imgSrc); 
    TagBuilder imgTagBuilder = new TagBuilder("img"); 
    imgTagBuilder.MergeAttribute("src", imgUrl); 
    string img = imgTagBuilder.ToString(TagRenderMode.Normal); 
    string url = urlHelper.Action(actionName); 
    
    TagBuilder tagBuilder = new TagBuilder("a")
                                {
                                    InnerHtml = img
                                }; 
    
    tagBuilder.MergeAttribute("href", url); 
    return tagBuilder.ToString(TagRenderMode.Normal);
}

The following does almost the same, but does it strongly-typed.

public static class HtmlHelperExtensions
{
    public static string ActionLinkWithImage<TController>(this HtmlHelper html, Expression<Action<TController>> action, string imgSrc) where TController: Controller
    {
        UrlHelper urlHelper = new UrlHelper(html.ViewContext.RequestContext); 
        string imgUrl = urlHelper.Content(imgSrc); 
        TagBuilder imgTagBuilder = new TagBuilder("img"); 
        imgTagBuilder.MergeAttribute("src", imgUrl); 
        string img = imgTagBuilder.ToString(TagRenderMode.Normal); 
        TagBuilder tagBuilder = new TagBuilder("a")
                                    {
                                        InnerHtml = img
                                    }; 
        tagBuilder.MergeAttribute("href", LinkBuilder.BuildUrlFromExpression(html.ViewContext.RequestContext, html.RouteCollection, action)); 
        return tagBuilder.ToString(TagRenderMode.Normal);
    }

    public static string ActionLinkWithImage<TController>(this HtmlHelper html, Expression<Action<TController>> action, string imgSrc, object imageAttributes, object linkAttributes) where TController : Controller
    {
        UrlHelper urlHelper = new UrlHelper(html.ViewContext.RequestContext);
        string imgUrl = urlHelper.Content(imgSrc);
        TagBuilder imgTagBuilder = new TagBuilder("img");
        imgTagBuilder.MergeAttribute("src", imgUrl);

        imgTagBuilder.MergeAttributes(new RouteValueDictionary(imageAttributes));

        string img = imgTagBuilder.ToString(TagRenderMode.Normal);
        
        TagBuilder tagBuilder = new TagBuilder("a")
        {
            InnerHtml = img
        };

        tagBuilder.MergeAttributes(new RouteValueDictionary(linkAttributes));
        tagBuilder.MergeAttribute("href", LinkBuilder.BuildUrlFromExpression(html.ViewContext.RequestContext, html.RouteCollection, action));
        return tagBuilder.ToString(TagRenderMode.Normal);
    }
}

An Example of consumption on your page would then be:

<%= Html.ActionLinkWithImage<FooController>(fc => fc.Edit(deal.ID), "~/Content/Images/edit_16.png", new { style = "border-style: none;", alt = "Edit Foo", title = "Edit Deal" }, null)%>

Hope this helps someone with the same predicament (Someone feel free to incorporate into the Futures assembly :))

# Saturday, June 13, 2009

ASP.NET MVC Form Validation Error with Decimal Parsing

Today I tried to put a relatively simple form together and was hoping to be done in a few minutes. Included on the form is an optional field asking for a monetary value. The type in the database is money and using LINQ-to-SQL the type is represented as a decimal.

All was well until the user entered a value that decimals generally don’t like to be converted into. Let’s assume ‘aaa’ for the time being. The result I was greeted with was this:

The model of type 'Foo' was not successfully updated.
My desire for ASP.NET MVC to handle this for me and put a nice validation message into the ModelState was just not happening.

My original code did this:
Foo foo = _modelRepository.FindFoo(id);

UpdateModel(foo);

The relatively simple fix is:
Foo foo = _modelRepository.FindFoo(id);

UpdateModel(foo, "SomeStringField", "SomeOtherStringField");

if (!TryUpdateModel(foo, "TheDecimalField"))
{
    ModelState.AddModelError("TheDecimalField", "Does that look like a number to you?");
}