Angular and ASP.NET MVC: now I've got two golden hammers!

Angular is a great tool, but it took me some time to find a way to combine it elegantly with ASP.NET MVC. This is basically how I did it.

First, create a new ASP.NET MVC application. Next, install the Angular package via NuGet. Now for the customization.

The objective is to use the normal ASP.NET MVC navigation, unless for certain URLs, when we'll let Angular take over.

So http://www.example.com/Account/Login would be handled by ASP.NET ("ASP.NET-mode"), but http://www.example.com/#/Customers would be handled by Angular ("Angular-mode"). Of course, it's ASP.NET serving us the Customers page, but after that, we want to use Angular for data-binding, navigation, routing, the forms, etc.

Add a new Controller with one method, Index(), that returns View(). Standard ASP.NET up until now. I named mine AngularController.

Next, add a View in the corresponding folder (in my case: /Angular/Index.cshtml). In this view, set up your main Angular view. Something like:

@{  
    ViewBag.Title = "Index";
}

<div ng-app="app">
    <div ng-controller="main as vm">
        <div ng-view class="shuffle-animation"></div>
    </div>
</div>

@section scripts {
    @Scripts.Render("~/bundles/angular")
}

So when we're in "Angular-mode", we want ASP.NET MVC to include our Angular scripts. The angular bundle looks something like this (in /App_Start/BundleConfig.cs):

bundles.Add(new Bundle("~/bundles/angular").Include(  
                      "~/Scripts/angular.js",
                      "~/Scripts/angular-animate.js",
                      "~/Scripts/angular-route.js",
                      "~/Scripts/angular-sanitize.js",
                      "~/Scripts/app/app.js",
                      "~/Scripts/app/config.js",
                      "~/Scripts/app/main.js",
                      "~/Scripts/app/customers/customers.js"));

The reason I'm not using a ScriptBundle is because we don't want ASP.NET to minify the Angular scripts. This causes errors because Angular sometimes depends on function arguments to be specific strings. You can read more on that here.

For now, minification isn't important, but in a production-environment, you would want to use the minified Angular scripts.

In app.js, config.js and main.js, I've put the necessary code to get Angular running. The most important part is the getRoutes function in config.js:

function getRoutes() {  
    return [
        {
            url: '/customers',
            templateUrl: '/Scripts/app/customers/customers.html'
        }
    ];
}

Finally, the customers.html and customers.js contain my Angular logic and HTML markup for this specific page. This now allows you to navigate to http://localhost:1748/Angular/#/ (your portnumber may vary of course).

There you have it. ASP.NET MVC is serving the HTML page that includes references to Angular scripts and templates, the browser downloads all that, and then Angular wires it all together!

(Of course, you might want to configure ASP.NET to use a different URL for the AngularController)

Adding this to your navigation is as simple as adding this tag to your _Layout.cshtml file:

<li><a href="https://www.blogger.com/Angular/#/customers">Customers</a></li>

Don't forget the hash.

Now lets add a second page. This will make the difference between what I've been calling "ASP.NET-mode" and "Angular-mode" more clear.

Add a new html file and a new javascript file to the /Scripts/app/customers/ folder, add the route to config.js and add the javascript file to the Angular bundle in BundleConfig.cs. The link in my case would now be:

<a href="https://www.blogger.com/Angular/#/customers/create">Create new customer</a>

Now, when you run the app, navigating from /Angular/#/customers to, say, /Account/Login will load the entire new page. But navigating from /Angular/#/customers to /Anguler/#/customers/create stays within Angular, and just loads the new template, "staying inside" your SPA. You can sort of notice it because loading a new page "inside" the SPA feels faster.

So we've effectively combined classic ASP.NET MVC with Angular, allowing us to choose where we want/need which.

You can check out the source code in my GitHub repository. I've taken care to make every commit a logical step in the process.