How can I define partial views with dynamic JQuery scripts if bundled scripts are rendered at the bottom of the page?

StackOverflow https://stackoverflow.com/questions/21504497

Question

I have a web application using MVC4 and Razor views that utilize a lot of JQuery and other plugin scripts.

For speeding up the rendering of views, I believe that in general, it is good practice to render the scripts at the bottom of the body, so the layout (much consolidated) looks like the following:

<body>
    @RenderBody()

    @Scripts.Render("~/bundles/jquery")
    @Scripts.Render("~/bundles/other")

    @RenderSection( "Scripts", false )
</body>

The Scripts section allows particular views to add additional JQuery functions, and they are guaranteed to be rendered after the JQuery library. So far, so good.

Now consider the following snippet in a view, which renders a partial for each item in a collection:

@foreach (var item in Model.Items)
{
    Html.RenderPartial("_MyPartial", item);
}

This partial (_MyPartial) needs to render JQuery for controls based on collection item details such as the following snippet:

$(function () {
    $("#@(Model.Id)").click(function () {
        // do something when control on partial with id Model.Id is clicked
        alert("I've been clicked!");
    });
});

The partial view JQuery functions do not get executed, because the JQuery library is not loaded prior to the _MyPartial scripts for each collection item. Running in chrome debugger shows $ not defined, which confirms this.

I could do something real ugly, such as replicating each foreach statement in the scripts section for all views that call the partial and then have the parent view generate the scripts. But, this in effect kills the re-usability of the partial view.

Is there a way to get Razor partial view scripts to render late (such as in parent scripts section), or a different approach to this issue?

Was it helpful?

Solution

Unfortunately sections are not supported in partial views. One possibility is to write custom helpers as I illustrated in this post: https://stackoverflow.com/a/9663249/29407

This would allow you to have a placeholder helper invoked at the end of the document which will render all scripts that have been registered in partials.

This being said, it is usually a better idea to expose the client side functionality that your partial needs as a jQuery plugin. Then it is trivially easy to invoke this plugin on some DOM elements:

$('.someElement').somePlugin();

This way you no longer need to be doing any $("#@(Model.Id)") in your partials. The partials shouldn't have any scripts in them normally.

OTHER TIPS

For reference, I used Darin's solution, and also extended it for cases in which I needed to register raw script that isn't located in a file. The additional methods are:

public static class HtmlExtensions
{
    public static IHtmlString RegisteredRawScripts(this HtmlHelper htmlHelper)
    {
        var ctx = htmlHelper.ViewContext.HttpContext;
        var registeredScripts = ctx.Items["_rawscripts_"] as Stack<string>;
        if (registeredScripts == null || registeredScripts.Count < 1)
        {
            return null;
        }
        var sb = new StringBuilder();
        foreach (var script in registeredScripts)
        {
            sb.AppendLine("\r\n" + script);
        }
        return new HtmlString(sb.ToString());
    }

    public static void RegisterRawScript(this HtmlHelper htmlHelper, string script)
    {
        var ctx = htmlHelper.ViewContext.HttpContext;
        var registeredScripts = ctx.Items["_rawscripts_"] as Stack<string>;
        if (registeredScripts == null)
        {
            registeredScripts = new Stack<string>();
            ctx.Items["_rawscripts_"] = registeredScripts;
        }
        if (!registeredScripts.Contains(script))
        {
            registeredScripts.Push(script);
        }
    }
}

In your partial, you do something like:

@Html.RegisterRawScript(script);  // where script is your raw script as a string

And in your layout, you register your raw scripts:

@Html.RegisteredRawScripts()
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top