How to pass HTML fragment (as a delegate?) to a declarative Razor Helper?

2.8k Views Asked by At

I've been writing some declarative Razor Helpers (using the @helper syntax) for use with the Umbraco 4.7 which now supports the Razor view engine (though I would imagine this applies equally to WebMatrix or ASP.NET MVC). They all work fine. However, I would like to make them a bit more flexible so that I can pass into them an HTML fragment that can be 'wrapped' around the output (but only when there is output). For instance, I have a helper (much simplified here) that can generate an HTML link from some parameters:

@helper HtmlLink(string url, string text = null, string title = null, 
string cssClass = null, bool newWindow = false)
{
    if (!String.IsNullOrEmpty(url))
    {
        System.Web.Mvc.TagBuilder linkTag = new System.Web.Mvc.TagBuilder("a");

        linkTag.Attributes.Add("href", url);
        linkTag.SetInnerText(text ?? url);

        if (!String.IsNullOrEmpty(title))
        {
            linkTag.Attributes.Add("title", title);
        }

        if (!String.IsNullOrEmpty(cssClass))
        {
            linkTag.Attributes.Add("class", cssClass);
        }

        if (newWindow)
        {
            linkTag.Attributes.Add("rel", "external");
        }

        @Html.Raw(linkTag.ToString())
    }
}

Calling @LinkHelper.HtmlLink("http://www.google.com/", "Google") would generate the HTML output <a href="http://www.google.com/">Google</a>.

What would be nice, though, is if I could optionally pass in an HTML fragment that would be wrapped around the generated hyperlink HTML so long as the URL has a value. I'd basically like to be able to do something like this:

@LinkHelper.HtmlLink("http://www.google.com/", "Google", @<li>@link</li>)

and get output

<li><a href="http://www.google.com/">Google</a></li>

or @LinkHelper.HtmlLink("", "", @<li>@link</li>)

and get no output at all.

I read in Phil Haacked's blog about Templated Razor Delegates but cannot get the hang of how they can be used in this context - if, indeed, it is possible. I get the feeling I'm missing something or barking up the wrong tree.

2

There are 2 best solutions below

3
On BEST ANSWER

I think the problem is with @link. Templated razor delegates take the data using a 'magic' parameter @item. Try replacing @link with @item in your template.

Also, post the code that executes the template - your HtmlLink method that takes Func<dynamic, object>.

0
On

In case anyone else is looking for this.. I put together the following which will work. It works for empty strings, and if the delegate is null (based on my not-quite-exhaustive testing below.)

The key is as Jakub says, to use the magic @item parameter.

@helper HtmlLink(string url, string text = null, 
    Func<IHtmlString, HelperResult> formatterFunction = null,  
    string title = null, string cssClass = null, bool newWindow = false)
{
    if (!String.IsNullOrEmpty(url))
    {
        System.Web.Mvc.TagBuilder linkTag = new System.Web.Mvc.TagBuilder("a");

        linkTag.Attributes.Add("href", url);
        linkTag.SetInnerText(text ?? url);

        if (!String.IsNullOrEmpty(title))
        {
            linkTag.Attributes.Add("title", title);
        }

        if (!String.IsNullOrEmpty(cssClass))
        {
            linkTag.Attributes.Add("class", cssClass);
        }

        if (newWindow)
        {
            linkTag.Attributes.Add("rel", "external");
        }

        // This is the part using the delegate
        if (formatterFunction == null)
        {
            @Html.Raw(linkTag.ToString())
        }
        else
        {
            @formatterFunction(Html.Raw(linkTag.ToString()))
        } 
    }
}

@HtmlLink("http://www.google.com", "Google")
@HtmlLink("http://www.google.com", "Google", @<b>@item</b>)
@HtmlLink("http://www.google.com", "Google", @<text><i>@item</i><br/></text>) @* <br/> fails otherwise *@
@HtmlLink("http://www.google.com", "Google", @<b>@item</b>)
@HtmlLink("", "", @<b>@item</b>)