0

Is it possible to implement a custom transformation on the output of a @helper? My point is to arrive at a solution that would allow me to have something along these lines:

@helper RenderCombinedStylesheetsWithColorOverride(string color)
{
    <link rel="stylesheet" type="text/css" href="menu.css" />
    <link rel="stylesheet" type="text/css" href="site.css" />
    <style type="text/css">
        * { color: @color; }
    </style>
}

The twist is in the name of the helper (RenderCombinedStylesheets...), which hints at what I'd like to do here. That is, take the normal HTML-encoded output of the helper, and pipe it through my custom code. In there, I'd like to take it apart and then reassemble it so that the final output is a single <link rel="stylesheet" ... /> reference to a generated combined and minified css file.

Note that this is a simplified example! In reality, there are multiple parameters and the output transformation is not limited to merely combining stylesheet fragments. I'd also like to do JavaScript code generation this way.

The main goal is to come up with something that will allow me to apply normal Razor templating to specific sections of my views and then perform additional transformations on those sections before emitting the final output.

Any ideas appreciated!

UPDATE: I've stumbled upon this SO question, which suggests a way to accomplish this is through plain ol' HtmlHelper extensions. It seems I've been having incomplete understanding of what they do, or at least underestimating their power. I'll report back with status on implementation.

Community
  • 1
  • 1
aoven
  • 2,248
  • 2
  • 25
  • 36

1 Answers1

0

Yes! The post I linked in my updated question was spot on! Here's the solution:

public static class ResourceCombiningHelper
{
    private static string MakeKey(string type)
    {
        return "CombinableResource" + (type ?? "").ToLowerInvariant();
    }

    public static IHtmlString CombinableResource(this HtmlHelper helper, dynamic template, string type)
    {
        var key = MakeKey(type);
        var context = helper.ViewContext.HttpContext;
        var resources = (List<dynamic>)context.Items[key];

        if (resources == null)
        {
            resources = new List<dynamic> { template };
            context.Items[key] = resources;
        }
        else
        {
            resources.Add(template);
        }

        return new HtmlString(String.Empty);
    }

    public static IHtmlString RenderCombinableResources(this HtmlHelper helper, string type)
    {
        var key = MakeKey(type);
        var context = helper.ViewContext.HttpContext;
        var resources = (List<dynamic>)context.Items[key];

        if (resources != null)
        {
            foreach (var resource in resources)
            {
                if (resource is HelperResult)
                {
                    // Add in-line content to combined resource here.
                }
                else if (resource is string)
                {
                    // Add external reference to combined resource here.
                }
            }
        }

        // Return a single reference to the combined resource (<link>, <script>, etc.)
    }
}

Usage in Razor view:

@helper ColorOverridingStyle(string color)
{
    <style type="text/css">
        * { color: @color; }
    </style>
}

@{ var color = ViewBag.TextColor; }
@Html.CombinableResource(Url.Content("~/Content/site.css"), "css")
@Html.CombinableResource(Url.Content("~/Content/menu.js"), "js")
@Html.CombinableResource(ColorOverridingStyle(color), "css")

And final HTML output example:

<head>
    <link href="/Content/combined-css.css" rel="stylesheet" type="text/css" />
    <script src="/Content/combined-js.js" type="text/javascript"></script>
</head>

Works great! Off to stretch the limits of this little gem... :)

aoven
  • 2,248
  • 2
  • 25
  • 36