4

Here is the situation :

On a MVC application I have a partial that is rendering a script bundle. this partial is rendered several times.

Is there a built-in way to determine an specific Bundle is rendered before in some place on page ?

Thanks professionals

Update : here is the code for clearing the condition

main layout :

<html>
  <body>
    @if(someCondition){
      @Html.RenderPartial("SamePartial")
      @Html.RenderPartial("SamePartial")
      @Html.RenderPartial("SamePartial")
    }
  </body>
</html>

and this is inside partial :

<div>Blah Blah Blah</div>

@Scripts.Render("~/bundles/JusNeededInsidePartial")
@Styles.Render("~/Themes/styles/JusNeededInsidePartial")

partial is rendered several times. I need a way to render bundles 1 time only

Manoochehr Dadashi
  • 715
  • 1
  • 10
  • 28

3 Answers3

2

Define a Razor section:

@section MySpecialScripts {
    @Scripts.Render("~/bundles/MySpecialScriptsBundle")
}

in views where you need such scripts and add the following to the main layout:

@if (IsSectionDefined("MySpecialScripts")) {
    RenderSection("MySpecialScripts")
}
Marcin Wachulski
  • 567
  • 4
  • 14
  • dear @devMarchine sections defined inside partials are not detected inside main layout. any suggestion? – Manoochehr Dadashi Jan 10 '14 at 12:29
  • Right, therefore I meant using views, not partial views (sorry for imprecision). I you really need partials, refer to: http://stackoverflow.com/a/9663249/3170952 (I know it's not built-in what you initially wanted.) – Marcin Wachulski Jan 10 '14 at 12:49
  • Thanks for link.unfortunately my code is kinda Generic. And I'm loading partials dynamically. I will have another problem with caching if I use the solution described in the link. I will post my final solution (if it be). – Manoochehr Dadashi Jan 10 '14 at 13:05
2

I'd be a bit leery about putting bundles inside your partial views like you are doing.

For best performance, I'd suggest putting scripts at the bottom of your page (ref: http://developer.yahoo.com/blogs/ydn/high-performance-sites-rule-6-move-scripts-bottom-7200.html)

If your bundles contain stylesheets, you're going to be putting them in the body, which is still valid, but I like to organize them so that they're all together.

Typically what I would do is add this to my _Layout.cshtml right before the closing </head> tag:

 @RenderSection("Head", required: false)

Then, in my View, I could have:

@Html.RenderPartial("SamePartial")
@Html.RenderPartial("SamePartial")
@Html.RenderPartial("SamePartial")

@section head{
    @Scripts.Render("~/bundles/myBundle")
}

And if I wanted to do something similar on another View:

@Html.RenderPartial("OtherPartial")
@Html.RenderPartial("OtherPartial")

@section head{
    @Scripts.Render("~/bundles/myOtherBundle")
}

I know this is a bit different than what you were asking for, but I think there's value in the flexibility of this solution.

And if you're loading things dynamically, you could always put a simple condition in your section:

@section head{
    @if(SomeCondition){
        @Scripts.Render("~/bundles/modernizr")
    }
}
Mister Epic
  • 16,295
  • 13
  • 76
  • 147
  • 1
    about rendering bundles inside partials in my project all I can say is : I have to render scripts inside partials. because I have several partials with several small scripts that each time page renders some of them. about your solution: Yes I am loading partials dynamically and I have to output-cache the partials too. with cached partials your solution will not work – Manoochehr Dadashi Jan 10 '14 at 23:11
1

You can do the check in the JavaScript scripts themselves:

In JavaScript you can check if something has been previously defined, and only define it if it hasn't.

For example:

if (typeof flag === 'undefined') {
    // variable is undefined
    flag = {};
    // Here type the code that must happen only once
}

NOTE: the flag name is a global var. To avoid collisions, you'd "namespace", and use specific names for each script file like this:

if (typeof myScripts === 'undefined') {
   myScripts = {};
}

if (typeof myScripts.MySpecialScripts=== 'undefined')
{
   myScripts.MySpecialScripts = '';
   // Add the "MySpecialScripts.js" code here
}

However, I'd render the script bundle outside the partials to ensure it's available to them. In this way you can ensure that it's rendered only once and available to all your partials.

In fact, unless it's a very large .js file, why not include it in your general bundle?

Yep, I know you feel tha your project is "better organized" if you include the scripts on the view which needs them, but that's not a good idea: you have to control if they have yet been included or not, you cand' move them to the bottom of the page if you want to and so on.

If you still want to keep your original idea you can use a the client dependency framework. It's not exactly what you want to do, but it's the best available solution. When you use this framework, you need to declare the dependencies of you components (Views) using different methods, like this:

  @{Html.RequiresJs("~/myscript.js");}

Anyway, as I wrote in my comment, the problem with this kind of solution is that if you start using AJAX there is no single way to get sure that you always get the desired scripts, and that they are available on client side when you need them. Unless you use some variation of the first client-side solution that I wrote in this answer (there are better solutions based on this basic concept). Let me insist: the best solution is always to have everything ready on the client side (unless it's a really huge project that have hundreds of big js files). Remember that the js file is loaded once, and cached for later use in the browser, so downloading a big .js file it's not such a big problem.

And please, even if you think you need a server side solution, don't discard a client side solution. Remember that, even if you server the js from the server, that's 'client side' code, so, why not use a 'client side solution' for your 'client side code' problem?

Still anothe solution: ypu can use PageData to share information between views, layouts, and partial views. When you need to add a script in any of this places, use a flag in PageData. If the flag is not present, render the script, and set the flag. If the flag is present, don't render the script. In this way, the script will be rendered only the first time.

if (PageData["JusNeededInsidePartial"] == null) // Check the flag
{
    @Scripts.Render("~/bundles/JusNeededInsidePartial");
    PageData["JusNeededInsidePartial"] = ""; // Set the flag
}

The problem with AJAX still exists, because if you render the partial by AJAX, it will load the script in the client side everything the partial view is loaded by AJAX.

JotaBe
  • 38,030
  • 8
  • 98
  • 117
  • :) Javascript is client side. we are talking about server side and rendering bundles in server side. javascrip is running inside broswer my dear friend. about rendering bundles inside partials in my project all I can say is : I have to render scripts inside partials. because I have several partials with several small scripts that each time page renders some of them. – Manoochehr Dadashi Jan 10 '14 at 23:03
  • 1st: JavaScript is client side, of course. But I'm showing you a way to solve your problem in the client side: let your server create all the javascript, and allow it to run only once with this technique. – JotaBe Jan 13 '14 at 12:35
  • 2nd: I've been developing with MVC for a long time, and belive me: doing what you want to do is quite hacky. Anyway, I'm editing my answer to show you a method that will allow you to do it, without all the problems associated with what you want to do (until you start using AJAX... that's when problems will arise, and that's why I dont' like things like these) – JotaBe Jan 13 '14 at 12:48
  • thanks for your detailed answer. I have no conflict inside scripts. and as you said I'm handling this kind of problems client side. and my scripts are not running more than once. I'm developing an enterprise project. I should have a server side protocol/framework to manage such things for performance matters and ... I should mention I'm Already using your server side solution. But this solution is not working when you cache the partial that is setting the flag. I was searching for a Built-in flag inside bundles and it seems there is no workaround for this. I will post my final solution – Manoochehr Dadashi Jan 13 '14 at 16:31
  • 1
    If it's a question of performance, allow me to insist on the client side solution: let all the partial generate the links to the necessary .js files. The browser will download them only once, anc cache them for later use. In this way, the server performance won't be impaired (the .js will be downloaded only once). The problem would be if loading the .js file more than once was a problem. And, as you know, there are ways to safeguard an already loaded . js file. If this still doesn't satisfy you, you can use donut caching, caching all the page but the part which renders the .js bundle. – JotaBe Jan 14 '14 at 11:42
  • 1
    By the way, if you want to improve server performance it's better to use the client side solution than the donut caching: with donut caching, the server will have still a little work to do: dynamically render the donut hole. Even if the donut hole is really small and simple, the client-side solution will use the cached copy of the whole view, so the server has nothing to do. Remember that the browser will not download the same .js file twice from the server, unless you make something wrong. You can check it with developer's tools (F12), Network tab, in most browsers. – JotaBe Jan 14 '14 at 11:45
  • I like your suggestion to leave the partials to link js files severally. browser cache will handle the rest. I think the best approach is this. also I think Donut caching will add many complexities to my system when I have several partials that are loading conditionally and then with Donut caching I should cache a part of partial and don't cache js part of it. (I mean in "my" already complex system it's not the best approach) – Manoochehr Dadashi Jan 14 '14 at 12:56
  • I'm trying to implement a server side class to manage scripts and styles on a page. maybe an action attribute or something to register scripts and styles. I know this way I'm tying the view to logic. But I think there is no better and easier way to do this. do you agree with this? – Manoochehr Dadashi Jan 14 '14 at 13:04