Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Clean alternative for not being able to use sections inside a partial view?

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?

like image 629
KidCode Avatar asked Apr 17 '17 09:04

KidCode


3 Answers

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.

like image 176
Joel Harkes Avatar answered Nov 04 '22 06:11

Joel Harkes


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.

like image 27
Tieson T. Avatar answered Nov 04 '22 05:11

Tieson T.


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>
like image 27
Paul Totzke Avatar answered Nov 04 '22 07:11

Paul Totzke