4

I need to have all my scripts at the bottom of the page, problem is when I have a Partial View I cannot use the "RenderSection" approach. Found a great example of how to add a HtmlHelper extension which takes the name of a script file, loads into a stack, then another helper renders that out on the base layout: Razor section inclusions from partial view

That's great - but I don't want to have to create an entire JS file for a little chunk of script, or maybe even HTML, that I want to drop in. And I don't want to pass it all as a string, I want the nice formatting and intellisense, so I want to use a template ie:

@{Html.AddScript("test", @<text>
<script type="text/javascript">
    function RefreshPreview() {
        $('#AutoReplyHtml_Preview').html(
            $('#htmlTemplate').html()
                .replace('@@MESSAGE_TITLE@@', $('#AutoReplySubject').val())
                .replace('@@PRE_HEADER@@', $('#AutoReplyPreHeader').val())
                .replace('@@MESSAGE_BODY@@', $('#AutoReplyHtml').val())
        );

        $('#AutoReplyPlainText_Preview').html(
            $('#plainTextTemplate').html()
                .replace('@@MESSAGE_BODY@@', $('#AutoReplyPlainText').val())
        );
    }

    $(document).ready(function() {
        RefreshPreview();
    });
</script>
</text>);}

Problem is - how to I get the value of the template into my method, I have this code which complies, but no clue how to get the data out of the "code" parameter:

    public static string AddScript(this HtmlHelper htmlHelper, string title, Func<object, object> code) {
    var ctx = htmlHelper.ViewContext.HttpContext;
    Dictionary<string, string> scripts = ctx.Items["HtmlHelper.AddScript"] as Dictionary<string, string>;
    if (scripts == null) {
        scripts = new Dictionary<string, string>();
        ctx.Items.Add("HtmlHelper.AddScript", scripts);
    }

    scripts.Add(title, code.ToString()); //Doens't work!

    return string.Empty;
}

How do I need to tweak the delegate parameter to get the value inside the template??

Community
  • 1
  • 1
Nayt Grochowski
  • 571
  • 1
  • 7
  • 19

1 Answers1

2

The helper architecture is designed so that it can accomodate scenarios where you are providing a template that will operate, for example, on each item in a list. In such a scenario, you'd of course want to be able to pass it the "current" item when iterating through the list.

However, in other scenarios (such as yours), there is no current item. Yet, as you've discovered, you still have to declare a delegate as a parameter to your method that defines a method that consumes one parameter. That's ok -- since you're not consuming that argument in your helper (you don't make use of the somewhat magical item parameter in your template) you can just pass it null in your implementation. Preferably, declare your parameter as a Func<object, IHtmlString> rather than a Func<object, object>, but regardless, just invoke code(null).ToString() to get the HTML-encoded string you need to render.

Kirk Woll
  • 76,112
  • 22
  • 180
  • 195
  • 2
    Perfect! that is exactly what I need - still very much a newbie when it comes to Func - just 1 tweak, had to make it IHtmlString: `public static IHtmlString AddBottomContent(this HtmlHelper htmlHelper, string title, Func code)` then the "code(null).ToString()" to dump it into a dictionary for later output worked! – Nayt Grochowski Jun 09 '12 at 02:07
  • Hmm, interesting. I use `HelperResult` in my code (which implements `IHtmlString`), but I'm glad you mentioned it; perhaps `IHtmlString` is more general and is what I should be using in my code. – Kirk Woll Jun 09 '12 at 02:14