7

Since we're not able to use sections inside a partial view, what's the cleanest approach to including scripts that are only required by the partial?

I know I can include my scripts section in the page calling the partial, but if my partial includes a Javascript component that I want to re-use throughout my site, then including custom scripts in every base page is a maintenance nightmare.

Considering that not being able to use JS inside a partial is by design, am I even correct in wanting to use a partial for a re-usable view that includes Javascript components?

3
  • u dont want to use it in containing page OR in partial. what exactly u are looking for? Commented Apr 17, 2017 at 10:26
  • I'd like to use it in the partial, but you can't. Commented Apr 17, 2017 at 10:38
  • @KidCode for javascript really the easiest method is to include it (bundled and minified ofc) in every page. Commented Apr 26, 2017 at 7:35

3 Answers 3

2
+50

You could make a partial signal to the layout page that it should include a script by for example using an IOC scoped or HttpContext Bag. which is read by the layout page.

The only, and main problem is that the script will only be in this bag once this the partial is executed and then most probably your html head section is already closed in the layout view. So its fine for adding scripts in the bottom of the page but it wont work for css.

A better way is to create your own kind of view compontents (or extend them) and make an option to specify necessary views. IOC these components at the top and render them where needed.

Better solution

A better solution would be, if like half of the pages need this java-script to bundle it inside the main bundle. The minifier would probably be able to minify more variables, and the user would need to request 1 less script (http request) for the page being loaded, thus creating less traffic, creating a faster page load, creating a better user experience.

Sign up to request clarification or add additional context in comments.

1 Comment

I think im with you in terms of bundling and minifying it into every page. - Not great for adhoc scripts but for the sake of simplicity it seems the way to go.
2

I won't pretend this is optimal, but if you don't want to create your own viewpage extensions, you might be able to get away with building something like this:

public static class DeferredScripts { static IList<string> paths = new List<string>(); public static IList<string> Scripts { get; set; } public static void Add(string path) { if (Scripts == null) { Scripts = new List<string>(); } if (!paths.Any(s => s.Equals(path, StringComparison.OrdinalIgnoreCase))) { var script = new TagBuilder("script"); script.Attributes.Add("src", path); Scripts.Add(script.ToString()); paths.Add(path); } } } 

Which you could then use like this in a partial view:

@{ DeferredScripts.Add("/scripts/testscript1.js"); } 

And use this in your layout (or in a view page):

@foreach (var script in DeferredScripts.Scripts) { @Html.Raw(script) } 

Seems to work in the test project I threw together: https://github.com/tiesont/DeferredScriptsTestApp

Telerik has something similar for their KendoUI ASP.NET MVC helpers, although I assume their implementation is more robust than what I've shown here.

1 Comment

It's a shame we have to resort to this sort of hack for something that seems so essential, but I really like your work around.
1

I think it would be fair but probably least helpful answer is to say "You're using the MVC6 wrong if you want to use sections in a partial or simulate it".

There are 3 main cases for an ideal solution to your problem:

  1. Partial/ViewComponent is added 0 times and the corresponding JavaScript library is added 0 times
  2. Partial/ViewComponent is added 1 time, the corresponding JavaScript library is added 1 time and the dynamically created initialization JavaScript is created once and placed after the library.
  3. Partial/ViewComponent is added many times, the corresponding JavaScript library is added 1 time and the dynamically created initialization JavaScript is created for each Partial/ViewComponent and are all placed after the library.

3 main options are

  1. add <hidden data-myTool-*="MyData"> and have a main script at the bottom of the page to read it all in (Not ideal because it violates #1)
  2. Make a DeferredScripts tool (Ideal but references an external class)
  3. Pass a list of strings to partials to add their script dependencies. (Ideal but can only easily work with non-layout views and assumes partials aren't using models)

This is an example of option 3:

Partial:

@model List<string> FROM TEST PARTIAL @{ Model.Add("Partial.js");} 

View:

@{ var jsfiles = new List<string>(); jsfiles.Add("View.js"); } @Html.Partial("_Test", jsfiles) @Html.Partial("_Test", jsfiles) @Html.Partial("_Test", jsfiles) @section scripts { @foreach (string file in jsfiles.Distinct().ToList()) { <script src="@file"></script> } } 

Result:

<script src="View.js"></script> <script src="Partial.js"></script> 

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.