2

What is the best way of adding external files from a partial view?

I need to do something like this

code in partial view:

@{
    var files = new List<string>();
    files.Add("some path");
    files.Add("some path");
    ViewBag.Files = files;
}

in layout page

@foreach (var file in ViewBag.Files) {
    @file
}

this does not actually work though

marcus
  • 9,616
  • 9
  • 58
  • 108
  • what will be the content of external files? `css`? `js`? wouldn't you be better with a simple `client.css` and one `client.js` where you can dynamically add whatever? - Let me know what kind of files and I will show you what I doing on my end. – balexandre Oct 06 '11 at 15:14
  • @balexandre I'm creating a simple cms where the developers should be able to create plugins as partial views (editor templates). The plugins can contain css och js for that specific plugin. Are you with me? – marcus Oct 06 '11 at 15:34
  • @balexandre can you show me how your solution works? – marcus Oct 08 '11 at 23:10

3 Answers3

6

As promised

You can't render @section's as it's not supported when rendered by Partial Views, until then you can do this trick:

in your _Layout.cshtml write

@RenderSection("scripts", false)
@Html.RenderSection("scripts")

The first one is the default, the second line is your brand new way to render a section, I use both in my code...

Now let's add some code to our Partial View

instead of

@section scripts { 
   <script src="@Url.Content("~/Scripts/jquery.validate.min.js")" type="text/javascript"></script>
   <script src="@Url.Content("~/Scripts/jquery.validate.unobtrusive.min.js")" type="text/javascript"></script>
}

replace it with:

@Html.Section(
    @<script src="@Url.Content("~/Scripts/jquery.validate.min.js")" type="text/javascript"></script>, "scripts"
)
@Html.Section(
    @<script src="@Url.Content("~/Scripts/jquery.validate.unobtrusive.min.js")" type="text/javascript"></script>, "scripts"
)

and our little helper that you just put inside your Models folder and reference it in your Partial View and Layout Page

// using idea from http://stackoverflow.com/questions/5433531/using-sections-in-editor-display-templates/5433722#5433722
public static class HtmlExtensions
{
    public static MvcHtmlString Section(this HtmlHelper htmlHelper, Func<object, HelperResult> template, string addToSection)
   {
      htmlHelper.ViewContext.HttpContext.Items[String.Concat("_", addToSection, "_", Guid.NewGuid())] = template;
      return MvcHtmlString.Empty;
   }

   public static IHtmlString RenderSection(this HtmlHelper htmlHelper, string sectionName)
   {
      foreach (object key in htmlHelper.ViewContext.HttpContext.Items.Keys)
      {
         if (key.ToString().StartsWith(String.Concat("_", sectionName, "_")))
         {
            var template = htmlHelper.ViewContext.HttpContext.Items[key] as Func<object, HelperResult>;
            if (template != null)
            {
               htmlHelper.ViewContext.Writer.Write(template(null));
            }
         }
      }
      return MvcHtmlString.Empty;
   }
}

To render CSS, all you need to so is using other section name, for example:

In _Layout.cshtml

@Html.RenderSection("styles")

in your Partial Views

@Html.Section(
  @<link rel="stylesheet" href="http://twitter.github.com/bootstrap/1.3.0/bootstrap.min.css">, "styles"
)

I hope this helps.

balexandre
  • 73,608
  • 45
  • 233
  • 342
  • this solution seems to work pretty good, I guess I need to put @Html.RenderSection("styles") before

    to get this to work properly because the partial view needs to be before the RenderSection.

    – marcus Oct 13 '11 at 21:07
  • Hi there, I have a late question regarding this solution. It seems like the sections i rendered in a random order? e.g. adding multiple scripts will cause some problems for me. – marcus May 20 '12 at 10:17
1

Instead of holding values in Viewbag try to hold values in HttpContext.Current.Items which is request scope.

Like this HttpContext.Current.Items.Add("files", files)

AnyOne
  • 931
  • 2
  • 12
  • 40
  • I would never use that as you can easily loose the context when dealing with several scenarios, for heavy stuff you should use `TempData`, but for holding a list of strings ... `ViewBag` or `ViewData` is more than enough! – balexandre Oct 06 '11 at 15:21
  • You can not access saved values in Layout if you use ViewBag or ViewData. TempData works till next request and it would cause wrong results. I wonder what kind of scenarios causing to lose context ? I actualy used this in my own application and worked well. – AnyOne Oct 06 '11 at 15:28
0

Maybe you can use sections for that. In your layout:

<head>
...
    @RenderSection("Head", false)
</head>

The false parameter indicates the section isn't required.

In your partial view you do something like this:

@section Head
{
    foreach(file in Model.Files}
    {
        .... render css/js links here
    }
}

This works for views, not entirely sure it works with partialviews, but it should :)

Robin van der Knaap
  • 4,060
  • 2
  • 33
  • 48