25

In a MVC3 project, I have a "_Layout.vbhtml" file with this code

<!DOCTYPE html>
<html>
  <head>
  </head>
  <body>
    ...
    <script src="@Url.Content("~/Scripts/jquery-1.8.2.min.js")"></script>
    @RenderSection("Scripts", false)
  </body>
</html>

Then, I have a Partial View "ValidationScripts.vbhtml" in the Shared folder with

@Section Scripts
    <script src="@Url.Content("~/Scripts/jquery.validate.min.js")"></script>
    <script src="@Url.Content("~/Scripts/jquery.validate.unobtrusive.min.js")"></script>
    <script src="@Url.Content("~/Scripts/jquery.validate.fix.js")"></script>  
    <script src="@Url.Content("~/Scripts/localization/messages_de.js")"></script>      
End Section

If I call the Partial View from a View like this...

@ModelType MvcExample.MyModel
@Code
    ViewData("Title") = "Test"
End Code

@Html.Partial("ValidationScripts")

<h2>Just a Test</h2>
...

the Section "Scripts" is not rendered on the page, and the output HTML is

<!DOCTYPE html>
<html>
  <head>
  </head>
  <body>
    ...
    <script src="@Url.Content("~/Scripts/jquery-1.8.2.min.js")"></script>

  </body>
</html>

How can I render the Section in the Partial View ?

Max
  • 4,965
  • 17
  • 49
  • 64

5 Answers5

46

I had the same issue on top of duplicate scripts, so I created a couple of Extension methods:

public static class HtmlHelperExtensions
{
  private const string _jSViewDataName = "RenderJavaScript";
  private const string _styleViewDataName = "RenderStyle";

  public static void AddJavaScript(this HtmlHelper htmlHelper, 
                                   string scriptURL)
  {
    List<string> scriptList = htmlHelper.ViewContext.HttpContext
      .Items[HtmlHelperExtensions._jSViewDataName] as List<string>;
    if (scriptList != null)
    {
      if (!scriptList.Contains(scriptURL))
      {
        scriptList.Add(scriptURL);
      }
    }
    else
    {
      scriptList = new List<string>();
      scriptList.Add(scriptURL);
      htmlHelper.ViewContext.HttpContext
        .Items.Add(HtmlHelperExtensions._jSViewDataName, scriptList);
    }
  }

  public static MvcHtmlString RenderJavaScripts(this HtmlHelper HtmlHelper)
  {
    StringBuilder result = new StringBuilder();

    List<string> scriptList = HtmlHelper.ViewContext.HttpContext
      .Items[HtmlHelperExtensions._jSViewDataName] as List<string>;
    if (scriptList != null)
    {
      foreach (string script in scriptList)
      {
        result.AppendLine(string.Format(
          "<script type=\"text/javascript\" src=\"{0}\"></script>", 
          script));
      }
    }

    return MvcHtmlString.Create(result.ToString());
  }

  public static void AddStyle(this HtmlHelper htmlHelper, string styleURL)
  {
    List<string> styleList = htmlHelper.ViewContext.HttpContext
      .Items[HtmlHelperExtensions._styleViewDataName] as List<string>;

   if (styleList != null)
   {
     if (!styleList.Contains(styleURL))
     {
       styleList.Add(styleURL);
     }
   }
   else
   {
     styleList = new List<string>();
     styleList.Add(styleURL);
     htmlHelper.ViewContext.HttpContext
       .Items.Add(HtmlHelperExtensions._styleViewDataName, styleList);
   }
 }

 public static MvcHtmlString RenderStyles(this HtmlHelper htmlHelper)
 {
   StringBuilder result = new StringBuilder();

   List<string> styleList = htmlHelper.ViewContext.HttpContext
     .Items[HtmlHelperExtensions._styleViewDataName] as List<string>;

   if (styleList != null)
   {
     foreach (string script in styleList)
     {
       result.AppendLine(string.Format(
         "<link href=\"{0}\" rel=\"stylesheet\" type=\"text/css\" />", 
         script));
     }
   }

  return MvcHtmlString.Create(result.ToString());
  }
}

On any View or Partial View or Display/Edit Template you simply add what you need:

@{
  Html.AddJavaScript("http://cdn.jquerytools.org/1.2.7/full/jquery.tools.min.js");
}

In your Layouts you render it where you want it:

<!DOCTYPE html>
<html lang="en">
  <head>
  @Html.RenderStyles()
  @Html.RenderJavascripts()

The only issue you may have is the order in which the scripts are rendered if you get to complex. If that becomes an issue, simply add the scripts to the bottom of your views/templates, and simply reverse the order in the extension method before rendering them.

Erik Philips
  • 53,428
  • 11
  • 128
  • 150
  • 2
    Anyone reviewing this using MVC 4 (or higher), you should probably look at using [Bundling Instead](http://www.asp.net/mvc/tutorials/mvc-4/bundling-and-minification). – Erik Philips Jul 21 '13 at 00:10
  • This won't work if you call the partial in your layout file. Html.AddJavaScript has already run by then. – LiverpoolsNumber9 Sep 11 '13 at 15:05
  • @LiverpoolsNumber9 only if you call `RenderJavaScripts()` before the partial view (tested). – Erik Philips Sep 11 '13 at 17:16
  • 1
    @ErikPhilips Yes, of course. But presumably you would be calling this in the head? If not, then calling it last thing in the layout would be preferable. To be safe, I would set a boolean value on "RenderJavaScripts()" that is then checked by "AddJavascript(..)" - that way you could throw an Exception if somebody has things upside down. You'd need the boolean value to have request context though obviously. – LiverpoolsNumber9 Sep 12 '13 at 08:32
  • 1
    Great extension. I've modified it for MVC4 where the extension functions now are taking virtual paths `params string[] paths` as argument – Neverever Sep 24 '13 at 06:50
  • For anyone coming to this later, there is a nuget package for doing this - http://www.nuget.org/packages/Forloop.HtmlHelpers/ – Russ Cam Feb 10 '14 at 11:45
  • @ErikPhilips This works great but triggers code validation (HtmlHelper does not contain definition for AddJavaScript), any way around that? nm, found it, just put @ using NameSpace.Utilities or whatever your class is at the top, thanks again (no space after at sign, SO is being weird). – fizgig Apr 04 '19 at 16:12
6

You can't use sections in partial views. You can go for custom helpers as mentioned here.

Community
  • 1
  • 1
VJAI
  • 32,167
  • 23
  • 102
  • 164
2

I think you should be using helpers for this http://weblogs.asp.net/scottgu/archive/2011/05/12/asp-net-mvc-3-and-the-helper-syntax-within-razor.aspx

If you can upgrade to MVC4 you could use the new bundling and minification feature: http://www.asp.net/mvc/tutorials/mvc-4/bundling-and-minification . It is specifically designed to address what you are trying to achieve (including scripts).

Alternatively you could use http://combres.codeplex.com/ with MVC3

Zar Shardan
  • 5,675
  • 2
  • 39
  • 37
1

If I understand correctly you have a structure

  • Layout.cshtml
  • View - X
    • PartialView Y (called inside View-X)

then you need to define the

@section Script{ .... } in the View-X and NOT PartialView Y since View-X has its View.Layout set to Layout.cshtml

frictionlesspulley
  • 11,070
  • 14
  • 66
  • 115
  • Of course, I can add the Section "Script" manually in any View. However if I want to group all the script tags in a place and reuse it in the Views, how can I do it? – Max Dec 07 '12 at 14:54
  • you could just render the partial in the layout itself if the scripts are being used by all views who use that layout OR if you would have to refer the layout in the partial! If you want to go that road you could try inheritance in the layouts where the layout which the partial uses just renders rest of the stuff as empty. Does that make sense ? – frictionlesspulley Dec 07 '12 at 15:00
  • What? :) Yes, I can follow that way but I think @Erik's extension methods are more useful. – Max Dec 07 '12 at 15:12
  • completely agree with that :) – frictionlesspulley Dec 07 '12 at 15:25
-3

all this was great info, however if you look at his original code, Section is capitalized therefore not being recognized.

it should be @section blahblah not @Section

Ethaan
  • 11,291
  • 5
  • 35
  • 45