Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

Angular JS and ASP.NET, All those Script Files

0.00/5 (No votes)
15 Oct 2014 1  
My way of diminishing the need to write out all those angular script files

Introduction

So I've recently ramped up my interaction with Angular JS. One of the first things that I thought had to be improved and made much much easier was how the scripts get added to your index page.

This is totally dependent on your approach to Angular JS, and currently with my approach, this is a solution to a problem that occurred for me.

I tackle Angular JS using the conventions set out in the Angular Seed App.

Angular Construction

File Structure

So, I separate out my files for each "area", one file for area.controllers.js, one file for area.services.js so I build up a file structure like so:

  • Angular\
    • Account\
      • account.controllers.js
      • account.services.js
    • Blog\
      • blog.controllers.js
      • blog.services.js
    • App.js

So with this method of naming conventions and separations, it makes it incredibly easy to find a particular place in my app. If I got an error, I can be like "that happened in my account page, let's get to Account -> account.controllers.js" and there is no need to scan one file for the line that specified .controllers('accountController', function ()).

Code Structure

In my files, I like to carry on the naming convention:

angular.module('account.controllers', [])  
.controllers('accountController', ['$scope', function ( $scope ) {

}]);

There are also other conventions I would like to point out, I use the dependency injection that Angular provides, apparently this helps .NET with minification. Also I use the [] in the module parameter, this instantiates a new module and in my mind a new scope.

So now you've seen [] you may ask how do you then use services that you create, etc.

Answer: I instantiate my Angular app like so:

angular.module('myApp', [  
    'ngRoute',
    'ngResources',
    'account.services',
    'account.controllers'
]); // usually I put the routing on here as well

This way, all the dependencies get loaded up and everything works.

ASP.NET Problem

The BundleConfig.cs is the big problem here. This is the code:

public class BundleConfig  
{
    // For more information on bundling, visit http://go.microsoft.com/fwlink/?LinkId=301862
    public static void RegisterBundles(BundleCollection bundles)
    {
        bundles.Add(new ScriptBundle("~/bundles/jquery").Include(
                    "~/Scripts/jquery-{version}.js"));

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

        // Use the development version of Modernizr to develop with and learn from. Then, when you're
        // ready for production, use the build tool at http://modernizr.com to pick only the tests you need.
        bundles.Add(new ScriptBundle("~/bundles/modernizr").Include(
                    "~/Scripts/modernizr-*"));

        bundles.Add(new StyleBundle("~/bundles/main/css").Include(
                    "~/Content/site.css",
                    "~/Content/font-awesome.css"));

        // LIST ALL ANGULAR FILES HERE
    }
}

Okay, so not the biggest problem in the world, you just create the bundle and add all the files to it.

bundles.Add(new ScriptBundle("~/bundles/angularapp").Include(  
                "~/Angular/Account/account.controllers.js"));

Even the small example here was a bit laborious to type out. This could easily be prone to mis-types.

One solution I have is this:

// BundleBuilder.cs
public static class BundleBuilder  
{
    public static Bundle Build(string bundleName, KeyValuePair<string, string[]>[] angularDictionary)
    {
        var virtualPaths = new List<string>();

        foreach (var reference in angularDictionary)
        {
            foreach (var file in reference.Value)
            {
                var bundle = new StringBuilder();
                bundle.Append("~/Angular/");
                bundle.Append(reference.Key);
                bundle.Append("/");
                bundle.Append(reference.Key.ToLower());
                bundle.Append(".");
                bundle.Append(file);
                bundle.Append(".js");

                virtualPaths.Add(bundle.ToString());
            }
        }

        var bundlePath = new StringBuilder();
        bundlePath.Append("~/bundles/");
        bundlePath.Append(bundleName.ToLower());

        return new ScriptBundle(bundlePath.ToString()).Include(virtualPaths.ToArray());
    }
}

// for the account bundles do this:
public static class AccountBundle  
{
    public static KeyValuePair<string, string[]> Scripts()
    {
        return new KeyValuePair<string, string[]>("Account", new[] { "controllers", "filter" });
    }
}

So now the BundleConfig.cs will look like this:

// FROM
bundles.Add(new ScriptBundle("~/bundles/angularapp").Include(  
                "~/Angular/Account/account.controllers.js"));

// TO 
bundles.Add(  
    BundleBuilder.Build(
        bundleName: "angularapp", 
        angularDictionary: new[]
            {
                AccountBundle.Scripts()
                // so on so fourth
            }));

Points of Interest

  1. Code Readability
  2. Repetition

This way is very readable, you know there is an AccountBundle for example and you know it generates the paths for the Scripts.

There are standards that you define when creating virtual paths and very little repitition. This minimises human error when writing the path out. Less of that "why isn't the whole functionality working, why hasn't the script been included, oh I missed a dot".

So now in the Razor view, we add this line:

<!-- Index.cshtml -->

@Scripts.Render("~/bundles/angularapp");

Conclusion

So all in all, a pretty neat solution, I'm not saying it's the best, or the most optimised, just something I whipped up on the fly.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here