Let's say I have a javascript slide-show in a partial view...
_Slideshow.cshtml:
@{
ViewBag.Title = "Slide Show";
}
<div id="slides">
</div>
<script src="js/slides.min.jquery.js"></script>
<script type="text/javascript">
$(function(){
$('#slides').slides({
// slide show configuration...
});
});
</script>
But I want to be a good little web developer, and make sure all of my scripts go at the bottom of the page. So I'll make my *_Layout.cshtml* page look like this:
_Layout.cshtml:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<title>@ViewBag.Title</title>
<link href="@Url.Content("~/css/global.css")" rel="stylesheet" type="text/css" />
</head>
<body>
<div id="wrapper">
@RenderBody
</div>
<!-- Being a good little web developer, I include my scripts at the BOTTOM, yay!
-->
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1/jquery.min.js" type="text/javascript"></script>
</body>
</html>
But UH OH! What do I do now, because my slide show script ends up above my jQuery inclusion?! It wouldn't be a big deal if I wanted my slide show on every page, but I only want the slide show partial view to be rendered on a certain page, and.... I WANT MY SCRIPTS AT THE BOTTOM! What to do?
You could define a section in your layout page for the scripts like this:
<body>
<div id="wrapper">
@RenderBody
</div>
<!-- Being a good little web developer, I include my scripts at the BOTTOM, yay!
-->
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1/jquery.min.js" type="text/javascript"></script>
@RenderSection("myScripts")
</body>
Then on your pages you define what goes in that section:
@{
ViewBag.Title = "Slide Show";
}
<div id="slides">
</div>
@section myScripts { //put your scripts here
<script src="js/slides.min.jquery.js"></script>
<script type="text/javascript">
$(function(){
$('#slides').slides({
// slide show configuration...
});
});
</script>
}
Then when the page renders, it will take everything in your section and add it to where it is supposed to go on your layout page (in this case, at the bottom).
Note: The accepted solution won't work for partial views as the question asks for.
In the normal flow, you can define the contents for a particular section from inside of the parent view on your ActionResult using a @section SectionName {}
declaration. And when that view is finally inserted into its LayoutPage, it can call RenderSection
to place those contents anywhere within the page, allowing you to define some inline JavaScript that can be rendered and parsed at the bottom of the page after any core libraries that it depends on like this:
The problem arises when you want to be able to reuse the full page view inside of a partial view. Perhaps you'd like to also re-use the view as a widget or dialog from inside of another page. In which case, the full Partial View is rendered in its entirety wherever you've placed the call to @Html.EditorFor
or @Html.Partial
inside of the Parent View like this:
According to the MSDN Docs on Layouts with Razor Syntax:
- Sections defined in a view are available only in its immediate layout page.
- Sections cannot be referenced from partials, view components, or other parts of the view system.
- The body and all sections in a content page must all be rendered by the layout page
In that scenario, it becomes tricky to get the script defined into the partial view to the bottom of the page. Per the docs, you can only call RenderSection
from the layout view and you cannot define the @section
contents from inside of a partial view, so everything gets lumped into the same area and your script will be rendered, parsed, and run from the middle of your HTML page, instead of at the bottom, after any libraries it might depend on.
For a full discussion of the many ways to inject sections from partial views into your page, I'd start with the following two questions on StackOverflow:
The varying solutions therein differ on support for nesting, ordering, multiple script support, different content types, calling syntax, and reusability. But however you slice it, pretty much any solution will have to accomplish two basic tasks:
Here's a simple implementation by Darin Dimitrov
Add the Helper Extension Methods which will allow you to build arbitrary script objects into the ViewContent.HttpContext.Items
collection and subsequently fetch and render them later.
Utilities.cs
public static class HtmlExtensions
{
public static MvcHtmlString Script(this HtmlHelper htmlHelper, Func<object, HelperResult> template)
{
htmlHelper.ViewContext.HttpContext.Items["_script_" + Guid.NewGuid()] = template;
return MvcHtmlString.Empty;
}
public static IHtmlString RenderScripts(this HtmlHelper htmlHelper)
{
foreach (object key in htmlHelper.ViewContext.HttpContext.Items.Keys)
{
if (key.ToString().StartsWith("_script_"))
{
var template = htmlHelper.ViewContext.HttpContext.Items[key] as Func<object, HelperResult>;
if (template != null)
{
htmlHelper.ViewContext.Writer.Write(template(null));
}
}
}
return MvcHtmlString.Empty;
}
}
Then you can use like this within your application
Build this script objects like this inside of your Partial View like this:
@Html.Script(
@<script>
$(function() {
$("#@Html.IdFor(model => model.FirstName)").change(function() {
alert("New value is '" + this.value + "'");
});
})
</script>
)
And then render them anywhere within your LayoutPage like this:
@Scripts.Render("~/bundles/jquery")
@RenderSection("scripts", required: false)
@Html.RenderScripts()
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With