20

Given MVC3 and Razor engine, I got

_MasterLayout.cshtml

@RenderSection("JavaScript", required: false)
..
..
@RenderBody()
..

View.cshtml with _MasterLayout.cshtml defined in _ViewStart.cshtml

..
@Html.RenderAction("PartialView", "PartialController")
..

PartialView.cshtml

..
@section JavaScript
{
........
}
..

How can I make sure that JavaScript from Partial View ends up in the Master Layout section?

Edit

The above scenario doesn't work because the partial view doesn't have a master layout defined. View, on the other hand, does have Layout with RenderSection defined. If I move section JavaScript from Partial View to View, Razor knows where to render it to. But, since partial view doesn't have a layout, it doesn't know what to do with section JavaScript and thus doesn't render it anywhere.

halfer
  • 19,824
  • 17
  • 99
  • 186
Dimskiy
  • 5,233
  • 13
  • 47
  • 66
  • I don't understand the question - does this not work? If not, why not? Do you get an error? Does RenderSection() get replaced with nothing? – Danny Tuppeny May 12 '11 at 16:58
  • @Danny Tuppeny - I added EDIT to the question, let me know if that makes sense. Thanks. – Dimskiy May 12 '11 at 17:05
  • I asked something similar a few weeks ago: http://stackoverflow.com/questions/5355427/populate-a-razor-section-from-a-partial – Craig M May 12 '11 at 19:24

3 Answers3

22

I created a set of extension methods to render scripts blocks (or anything) from a partial, into the main layout.

public static class ViewPageExtensions
{  
 private const string SCRIPTBLOCK_BUILDER = "ScriptBlockBuilder";

    public static MvcHtmlString ScriptBlock(
        this WebViewPage webPage,
        Func<dynamic, HelperResult> template)
    {
        if (!webPage.IsAjax)
        {
            var scriptBuilder = webPage.Context.Items[SCRIPTBLOCK_BUILDER] 
                                as StringBuilder ?? new StringBuilder();

            scriptBuilder.Append(template(null).ToHtmlString());

            webPage.Context.Items[SCRIPTBLOCK_BUILDER] = scriptBuilder;

            return new MvcHtmlString(string.Empty);
        }
        return new MvcHtmlString(template(null).ToHtmlString());
    }

    public static MvcHtmlString WriteScriptBlocks(this WebViewPage webPage)
    {
        var scriptBuilder = webPage.Context.Items[SCRIPTBLOCK_BUILDER] 
                            as StringBuilder ?? new StringBuilder();

        return new MvcHtmlString(scriptBuilder.ToString());
    }
}

Then it can be used like so:

@this.ScriptBlock(
@<script  type='text/javascript'>
    $(document).ready(function () {
        notify('@message', '@Model.Type.ToString()',                    
               '@Model.Type.ToString().ToLower()');
    });
</script>)

And then rendered in the main layout:

@this.WriteScriptBlocks()

Check out the full article here: A Script-Block Templated Delegate for Inline-Scripts in Razor Partials

amit_g
  • 30,880
  • 8
  • 61
  • 118
Jason Wicker
  • 3,416
  • 2
  • 25
  • 32
  • 3
    The mark of a good answer isn't just a link to an article which by your own admission "went away" for a while. Why not summarise the post and include code samples so that if the link fails again future users aren't left in the dark. Also, be careful when posting copy and paste boilerplate/verbatim answers to multiple questions, these tend to be flagged as "spammy" by the community. – Kev Jul 12 '11 at 23:26
  • 1
    Thanks for the feedback. I will add the details you mentioned. – Jason Wicker Jul 12 '11 at 23:36
  • 1
    i really like your idea. Thank you! – Alexandre Jobin Aug 26 '11 at 13:43
  • How do you manage rendering order? I mean, what if some of those `ScriptBlock` calls happen AFTER the `WriteScriptBlocks` call? – Fyodor Soikin Dec 17 '11 at 03:51
  • what happends if the pages is rendered without layout for example as a partial request from client side code – Souhaieb Besbes Apr 14 '16 at 14:47
  • 1
    I reread you implementation, it is simple yet brilliant, this should be on the default mvc implementation. – Souhaieb Besbes Apr 18 '16 at 08:06
7

I don't believe a Partial View can set sections to be used in the layout page :(

It looks like there are no nice solutions to this - you could include two partials (one for script, one for content):

<script stuff>
@Html.RenderAction("PartialViewScripts", "PartialController")
</script stuff>
<body stuff>
@Html.RenderAction("PartialView", "PartialController")
</body stuff>
Danny Tuppeny
  • 40,147
  • 24
  • 151
  • 275
  • Would that be in the View.cshtml? – Dimskiy May 12 '11 at 17:14
  • That's right, yes. And you'd need two Actions on your PartialController - one for the view, and one for the scripts. It's not particularly nice, but I can't think of a better way right now :( – Danny Tuppeny May 12 '11 at 17:19
  • Yeah, I was thinking of a similar approach while waiting for an answer. Basically, I have a conditional inside of the view. If it's true, I render partial. I'm just going to add the js section to inside of that conditional, without making second partial view just for js. Thank you for the answer. – Dimskiy May 12 '11 at 17:23
0

This worked for me allowing me to co-locate javascript and html for partial view in same file for ease of readability

In View which uses Partial View called "_MyPartialView.cshtml"

<div>
    @Html.Partial("_MyPartialView",< model for partial view>,
            new ViewDataDictionary { { "Region", "HTMLSection" } } })
</div>

@section scripts{

    @Html.Partial("_MyPartialView",<model for partial view>, 
                  new ViewDataDictionary { { "Region", "ScriptSection" } })

 }

In Partial View file

@model SomeType

@{
    var region = ViewData["Region"] as string;
}

@if (region == "HTMLSection")
{


}

@if (region == "ScriptSection")
{
        <script type="text/javascript">
    </script">
}
purvin
  • 61
  • 2