85

I'm trying to use a specific locale (es-CL) in my ASP.NET MVC 5 application. I've the following:

  1. Changed web.config uiculture and culture to "es-CL"
  2. Installed the Globalize and jQuery.Validation.Globalize packages
  3. Changed the default language in my views: <html lang="es-cl">
  4. Created a new Bundle and included in the appropriate views.

In BundleConfig.cs:

bundles.Add(new ScriptBundle("~/bundles/jqueryval")
    .Include("~/Scripts/jquery.validate.js")
    .Include("~/Scripts/jquery.validate.unobtrusive.js"));

bundles.Add(new ScriptBundle("~/bundles/globalization")
    .Include("~/Scripts/globalize/globalize.js")
    .Include("~/Scripts/globalize/cultures/globalize.culture.es-CL.js")
    .Include("~/Scripts/jquery.validate.globalize.js"));

In the appropriate views:

@section Scripts {
    @Scripts.Render("~/bundles/jqueryval")
    @Scripts.Render("~/bundles/globalization")
}

However, the generated source code is the following:

<script src="/Scripts/jquery.validate.js"></script>
<script src="/Scripts/jquery.validate.unobtrusive.js"></script>

<script src="/Scripts/jquery.validate.globalize.js"></script>
<script src="/Scripts/globalize/globalize.js"></script>
<script src="/Scripts/globalize/cultures/globalize.culture.es-CL.js"></script>

Please note that the jquery.validate.globalize.js script is being loaded before globalize.js, which is not what I want.

Why is this happening? Is it possible to rely in the include order in a single bundle, or am I forced to put this single script in a different bundle and specify it in my view?

Sparky
  • 98,165
  • 25
  • 199
  • 285
Leonardo Herrera
  • 8,388
  • 5
  • 36
  • 66
  • 1
    Have a look at this question http://stackoverflow.com/questions/11979718/how-can-i-specify-an-explicit-scriptbundle-include-order – PMC Oct 11 '13 at 17:03
  • @PaulMcCowat yes, but I'm not using the minified versions yet. I'm using Microsoft.AspNet.Web.Optimizations 1.1.0. – Leonardo Herrera Oct 11 '13 at 17:08
  • @LeonardoHerrera I would think it is like Chris mentioned in his comments, that known files are moved around to an order specified by the bundler... but I can't be sure since I don't know what those files are, which [led me to ask](http://stackoverflow.com/questions/19325487/ordering-of-files-within-a-bundle-what-are-the-known-libraries). I'll be interested to know if using the IBundleOrderer works for you since specifying the order didn't. – MikeSmithDev Oct 11 '13 at 19:36
  • look at @Softlion answer in [this link][1] [1]: http://stackoverflow.com/questions/11979718/how-can-i-specify-an-explicit-scriptbundle-include-order – Omid.Hanjani Jun 17 '15 at 07:02
  • `@section Scripts { @Scripts.Render("~/bundles/jqueryval") }` appears to have resolved the issue of my scripts loading out of order.... – petrosmm Nov 10 '16 at 16:25
  • Today I learned MVC's default bundling doesn't actually bundle anything. – AaronLS Aug 28 '19 at 20:39

3 Answers3

104

By default, bundling order is alphabetical for names with wildcards (as pointed out in the comments). However, it also orders based on what it thinks your dependency tree is, and jQuery scripts seem to get slotted to the top. You need to create an object that implement IBundleOrder:

class NonOrderingBundleOrderer : IBundleOrderer
{
    public IEnumerable<FileInfo> OrderFiles(BundleContext context, IEnumerable<FileInfo> files)
    {
        return files;
    }
}

This prevents the default ordering. Now to use it:

var bundle = new ScriptBundle("~/bundles/globalization")
    .Include("~/Scripts/globalize/globalize.js")
    .Include("~/Scripts/globalize/cultures/globalize.culture.es-CL.js")
    .Include("~/Scripts/jquery.validate.globalize.js");

bundle.Orderer = new NonOrderingBundleOrderer();

bundles.Add(bundle);

ref: http://stevescodingblog.co.uk/changing-the-ordering-for-single-bundles-in-asp-net-4/

For further reading, an answer to MikeSmithDev's question provides further insight into the default ordering for popular script libraries:

Ordering of Files within a bundle - What are the known libraries?

Community
  • 1
  • 1
Mister Epic
  • 16,295
  • 13
  • 76
  • 147
  • Not to nitpick, but "by default, bundling order is alphabetical" is true when you use things like wild cards... when you specify the order, like he has, it should use his order. I can only assume the bundler is moving around known file types and is ignoring his order. – MikeSmithDev Oct 11 '13 at 17:54
  • It looks like the bundler also orders using some logic about dependencies. It thinks that `jquery.validate.globalize.js` is required for the other two. From the ref: "[The bundler] will even put known framework javascript files first in the bundle automatically, such as jQuery or Prototype scripts, to make sure they run before your own code which uses their types gets executed" – Mister Epic Oct 11 '13 at 18:37
  • Yes, that's what I was referring to. I know it does this for certain files... so I can only assume those globalization scripts are some of those known file types. It'd be nice if they gave a list of the files / order they'll be put in... I've never seen one. – MikeSmithDev Oct 11 '13 at 18:57
  • 3
    The spesific order is: jquery.js jquery-min.js jquery-* jquery-ui* jquery.ui* jquery.unobtrusive* jquery.validate* modernizr-* dojo.* mootools-core* mootools-* prototype.js prototype-* scriptaculous-* ext.js ext-* – ravndal Feb 19 '14 at 12:48
  • How would you accomplish this in VB.NET? I tried changing the code to VB but it doesn't work – Mark Pieszak - Trilon.io Feb 24 '14 at 21:35
  • @mcpDESIGNS `Class NonOrderingBundleOrderer` `Implements IBundleOrderer` `Public Function OrderFiles(context As BundleContext, files As IEnumerable(Of BundleFile)) As IEnumerable(Of BundleFile) Implements IBundleOrderer.OrderFiles` `Return files` `End Function` `End Class` worked for me – user1069816 Mar 21 '14 at 16:01
  • 6
    here is where programming is not fun. why VS make this complicated? – Jaider Nov 08 '14 at 19:00
  • 5
    The fact that this exists blows my mind with how stupid it is. Really.. the bundler knows better how to order my dependencies better than I've typed them? Gulp.js (or even Grunt) FTW! – John Culviner Apr 27 '16 at 01:59
  • @JohnCulviner The `IBundleOrderer` is being made irrelevant. Fortunately with VS 2015, we can absolutely use Gulp and Grunt for these sorts of things. We can even leverage WebPack with a little more work! – Mister Epic Sep 29 '16 at 16:04
  • Definetly. I'm glad Microsoft is getting on the OSS train rather than trying to come up with their own half baked implementation of everything that is buggy and not maintained :) – John Culviner Sep 30 '16 at 15:30
31

In the last version of MVC 5 (at october 27 of 2014), yo should use this class instead:

class AsIsBundleOrderer : IBundleOrderer
{
    public IEnumerable<BundleFile> OrderFiles(BundleContext context, IEnumerable<BundleFile> files)
    {
        return files;
    }
}

And create the bundle like the other response:

var bundle = new ScriptBundle("~/bundles/globalization")
.Include("~/Scripts/globalize/globalize.js")
.Include("~/Scripts/globalize/cultures/globalize.culture.es-CL.js")
.Include("~/Scripts/jquery.validate.globalize.js");

bundle.Orderer = new AsIsBundleOrderer();

bundles.Add(bundle);
Sebastián Rojas
  • 2,776
  • 1
  • 25
  • 34
30

To reduce the codes during creating bundles, I suggest you create an extension method.

Require infrastructure classes:

class NonOrderingBundleOrderer : IBundleOrderer
{
    public IEnumerable<BundleFile> OrderFiles(BundleContext context, IEnumerable<BundleFile> files)
    {
        return files;
    }
}


static class BundleExtentions
{
    public static Bundle NonOrdering(this Bundle bundle)
    {
        bundle.Orderer=new NonOrderingBundleOrderer();
        return bundle;
    }
}

Now simply use it like this:

All in one command

bundles.Add(new ScriptBundle("~/bundles/jqueryval")
               .NonOrdering()
               .Include(
                    "~/Scripts/globalize/globalize.js",
                    "~/Scripts/globalize/cultures/globalize.culture.es-CL.js",
                    //...
                );
Ramin Bateni
  • 16,499
  • 9
  • 69
  • 98