Angular JS took the Web development world by storm. Overnight, writing Single Page Applications (SPAs) was the hot new thing and everyone wanted to do it. Most people set out to learn via books, blogs, and video courses, but there's one problem with that. Angular is a Google product and has no dependency on the Microsoft stack. I'm not saying this as a negative statement, it's just a fact. Most training uses other editors and teaches you how to use HTML and JavaScript (Angular), period. But many of us are Microsoft developers and have a lot invested in the Microsoft stack, so we shouldn't have to turn away from it in order to jump on the Angular bandwagon.

If you start to look into typical Angular training, you'll learn how to ditch Visual Studio, learn Web Storm and Node, and dive head first into the world of HTML and JavaScript without many - if any - other dependencies or additional technologies. This isn't totally bad because if you're going to learn something like Angular, you do need to learn it in full and understand all its parts intimately. This article is not about that. This article won't teach you Angular from the ground up. There's a lot of great material, both printed and digital about Angular and how to work with it. This article is about showing you how to leverage your ASP.NET MVC skills together with your Angular skills in order to have your cake and eat it too. Not only will I show you how to integrate the two technologies into a hybrid app, but I'll also teach you my design and organization style that I use everywhere I have to build these kinds of applications, including file location choices and coding conventions that I hope you'll also find beneficial.

The Traditional SPA

Frameworks like Angular, Ember, Backbone, and even Knockout are used to build something that's become known as a Single Page Application, or SPA. IMHO, this is one of the most misused and dangerous terms I've seen come around in a long time. You see, the sad truth is that many people take the terms they hear very literally, and in this case, they set out to write a Web application that is indeed one single page with just a lot of view templates swapped in and out of a placeholder. If the term SPA is to be taken literally and we all set out to write our Web applications in this fashion, things start out nice, pretty, and simple at first, but as the application grows, all hell can break loose. Why would I say something like that? Well, because literal SPAs have some limitations and can grow to be hard to manage, especially in a team environment.

The Single Layout Shell (w/o ASP.NET)

I'm going to start by explaining and illustrating a traditional Angular SPA. Keep in mind that I won't be explaining what views and view-models are or how MVVM binding works. I'll be assuming that anyone reading this has Angular knowledge already, and later I'll be making the same assumption about ASP.NET MVC skills. By starting with a single layout SPA, and then building on my examples to show you how to handle a more complex site with more than one layout, I'll be able to describe the problems I'll solve later when I introduce ASP.NET MVC into the mix.

A typical Angular-based SPA has an HTML view that sets up the layout of the website and a template placeholder noted by the ng-view directive. This HTML page also needs to load up any necessary JavaScript file, including, but not limited to, the Angular framework itself, the site's module, and all required view-models. What I call view-models are, in fact, what Angular refers to as controllers. Listing 1 shows you a sample Angular shell view called Index.html. Notice that it uses an Angular module called appMain along with an Angular view-model called indexViewModel. I refer to the Index.html view as the “shell view” because its content defines the shell layout for this simple SPA site.

Listing 1: A simple Angular shell view

<html data-ng-app="appMain" data-ng-controller="indexViewModel">
    <head>
        <title>{{ headingCaption }}</title>
        <link href="Content/bootstrap.min.css" rel="stylesheet" />
    </head>
    <body>
        <h2>{{ headingCaption }}</h2>
        <hr />
        <div ng-view></div>        

        <script src="Scripts/<a href="http://angular.min.js">angular.min.js</a>"></script>
        <script src="Scripts/<a href="http://angular-route.min.js">angular-route.min.js</a>"></script>
        <script src="Scripts/App.js"></script>
    </body>
</html>

What I call view-models are, in fact, what Angular refers to as controllers.

The layout for this site is very simple. It consists of a heading provided by the variable headingCaption, a horizontal line, and some content beneath it. The content swapped into the ng-view directive comes from various HTML templates, each binding to their own view-model. And of course, this is all configured in the module setup using Angular's routing capability, as shown in Listing 2 along with the indexViewModel view-model that's bound to this layout view in its <html> tag.

Listing 2: A simple Angular module and routes

var appMainModule = angular.module('appMain', ['ngRoute'])
    .config(function ($routeProvider, $locationProvider) {
        $routeProvider.when('/', {
            templateUrl: '/Templates/Home.html',
            controller: 'homeViewModel'
        });
        $routeProvider.when('/customer/list', {
            templateUrl: '/Templates/CustomerList.html',
            controller: 'customerListViewModel'
        });
        $routeProvider.when('/customer/detail', {
            templateUrl: '/Templates/CustomerDetail.html',
            controller: 'customerDetailViewModel'
        });
        $routeProvider.otherwise({ redirectTo: '/' });
        $locationProvider.html5Mode({
            enabled: true,
            requireBase: false
        });
});

appMainModule.controller("indexViewModel", function ($scope, 
                         $http, $location) {
    $scope.headingCaption = 'Customer Maintenance';
});

Basic Routing

Each one of the HTML templates displays not only content but also links that route to other views. These links are intercepted by the routes defined in Listing 2 and the appropriate view and view-model is brought together and replaced as the content in the ng-view placeholder. The first route in the routing definition shows what view and view-model is used when the routing path is a simple "/", meaning the root of the application. The views for the single-shell-layout app are shown in Listing 3 and the rest of the view-models are in Listing 4.

Listing 3: Views Single Shell Layout App

                             Home.html

<h2>{{ heading }}</h2>
<br />
<br />
<a href="/customer/list">Go to customer list view</a>
<br />
<a href="/customer/detail">Go to customer detail view</a>

                         CustomerList.html

<h3>{{ heading }}</h3>
<br />
<table class="table table-hover">
    <thead>
        <tr>
            <th></th>
            <th>First Name</th>
            <th>Last Name</th>
        </tr>
    </thead>
    <tbody ng-repeat="p in people">
        <tr>
            <td>
                <a href="#" ng-click="showPerson(p)">select</a>
            </td>
            <td>{{ p.FirstName }}</td>
            <td>{{ p.LastName }}</td>
        </tr>
    </tbody>
</table>
<br />
<br/>
<a href="/customer/detail">Go to customer detail view</a>

                       CustomerDetail.html

<h3>{{ heading }}</h3>
<br />
<a href="/customer/list">Go to customer list view</a>

Listing 4: All other View-Models for Single Shell Layout App

appMainModule.controller("homeViewModel", function ($scope,
                         $http, $location) {
    $scope.heading = 'This is the Home view.';
});

appMainModule.controller("customerListViewModel", function (
                         $scope, $http, $location) {
    $scope.heading = 'Customer List View';

    $scope.people = [
    { FirstName: 'Miguel', LastName: 'Castro' },
    { FirstName: 'Rod', LastName: 'Paddock' },
    { FirstName: 'Sahil', LastName: 'Malik' },
    { FirstName: 'John', LastName: 'Petersen' },
    { FirstName: 'Kevin', LastName: 'Goff' }
    ];

    $scope.showPerson = function (person) {
        alert('You selected ' + person.FirstName + ' ' +
              person.LastName);
    }
});

appMainModule.controller("customerDetailViewModel", function 
                         $scope, $http, $location) {
    $scope.heading = 'Customer Detail View';
});

Take a look at the first view in Listing 3, Home.html. As you can see, it shows a heading variable and two simple links to the routes /customer/list and /customer/detail. I'll use the first link to describe what happens. This route gets picked up by Angular and runs through the routing table, and the result is the display of the CustomerList.html view bound to the customerListViewModel view-model.

The ngRoute Angular module is necessary for angular routing to function.

It's important to understand the order of operation here as it will become even more important in the next section, and is crucial to understand when I begin involving ASP.NET MVC. The initial browsing must happen to Index.html. Until this page loads, Angular hasn't loaded and the routing tables haven't been defined. Once this page is rendered, Angular takes over and remembering the initial browsing URL ({host}/Index.html), it runs that route through the routing table. Because there's no route that specifies that page, the otherwise clause is hit and the route switches over to /customer. This not only loads the CustomerHome.html template and binds it to the homeViewModel view-model, but it also has the effect of changing the URL that appears on the browser bar to /customer. Without some additional help, I can't “friendly-up” the URLs any further for that initial browsing experience. There is no ASP.NET involved here at all, so the browsing is physically performed directly to the Index.html page.

You can add as many views/view-models as you need to continue building this single-shell SPA application. Each requires an entry in the module's routing definition and each requires the shell view, Index.html, to load its necessary JavaScript file. In the example, all of the view-models are contained in the App.js file that's loaded by the shell view, but remember that this file can grow and grow and grow as the application gets larger. The first drawback here is that your user may never get to use some of the JavaScript view-models because he never even gets to a particular view. In small apps, this may not be a problem, but in larger ones, it can result to the unneeded loading of a lot of JavaScript that may never be used.

This can easily become the case if the application grows to a point where there are different sections, for example, Customer, Product, and Order sections. The second drawback is that this single-shell application is, in fact, a SPA in its entirety. This means that I can add views for product- and order-based information and their corresponding view-models, and all of them load into the existing ng-view directive that you saw in the index.html file. This limits you to a single layout for the entire site. That sounds fine, except that now we're talking about multiple sections of the application. It's normal for a site as a whole to share a common theme, but it's also very possible for different site sections to have a sort-of sub-theme within themselves. Perhaps you want the Product section of the site to show some fixed information on the left and the switchable content on the right, whereas the Customer section is to show some fixed information on the right with the switchable content on the left. With the existing stack, you're now forced to have multiple shell views. The challenge there is code-repetition and navigation.

First of all, I now need more than one shell view. Secondly, to navigate to that shell view, I need a server trip. This isn't a problem; I just need to know that the navigation currently taking place with the anchor tags is being routed by the client through the Angular routing engine. Obviously, this means it isn't doing a server trip and any view that's loaded gets placed in the ng-view placeholder. This is not what I want if I need to do what will now become a kind of cross-shell-view navigation.

Multi-SPAs

What I'm really after here is two separate SPAs, if you will. One for the Customer section of my site and another for the Product section. I'm after two separate sections because the shell layout of each one will be different. If I make the Customer and Product sections both as part of my original shell and add navigation information to the Angular routing definition, I'm forced to share the visible layout as defined by my single shell view.

Multiple Layouts

Using raw HTML and Angular JS, I can break up my application into sections, each with its own shell layout. Each section is, in effect, a SPA. What's important to understand here is that each section is rendered with a server trip, just like the Index.html page in my previous example, and everything housed within it is handled by the client. I've set up two index pages, called CustomerIndex.html and ProductIndex.html and they can be found in Listing 5 and Listing 6. Note that each of these represents a fully valid HTML page, complete with html, head, and body tags, and each loads up the appropriate Script and Content files. Each of these two pages is also the container for the Angular ng-view directive, but notice that the layout slightly varies. The Customer page has some navigation links on the left, with the ng-view on the right. The Product page has them reversed. I made the difference in layout simple for the sake of the article, but this could have just as well been a massive visual difference between Customer and Product sections, complete with images and everything. Navigation within and without of the current SPA occurs a little different, as I'll explain soon.

Listing 5: CustomerIndex.html

<html data-ng-app="appCustomer" data-ng-controller="customerIndexViewModel">
    <head>
        <title>{{ headingCaption }}</title>
        <link href="Content/bootstrap.min.css" rel="stylesheet" />
    </head>
    <body>
        <h2>{{ headingCaption }}</h2>
        <hr />
        <div class="row">
            <div class="col-md-3">
                <a href="/customer/list">
                    Go to customer list view
                </a>
                <br />
                <a href="/customer/detail">
                    Go to customer detail view
                </a>
                <br />
                <a href="/ProductIndex.html" target="_self">
                    Go to the product home page (link-based)
                </a>
                <br />
                <a href="#" ng-click="ProductHome()">
                    Go to the product home page
                </a>
            </div>
            <div class="col-md-9">
                <div ng-view></div>
            </div>
        </div>

        <script src="Scripts/<a href="http://angular.min.js">angular.min.js</a>"></script>
        <script src="Scripts/<a href="http://angular-route.min.js">angular-route.min.js</a>"></script>
        <script src="Scripts/AppCustomer.js"></script>
    </body>
</html>

Listing 6: ProductIndex.html

<html data-ng-app="appProduct" data-ng-controller="productIndexViewModel">
    <head>
        <title>{{ headingCaption }}</title>
        <link href="Content/bootstrap.min.css" rel="stylesheet" />
    </head>
    <body>
        <h2>{{ headingCaption }}</h2>
        <hr />
        <div class="row">
            <div class="col-md-3">
                <a href="/product/list">
                    Go to product list view
                </a>
                <br />
                <a href="/product/detail">
                    Go to product detail view
                </a>
                <br />
                <a href="/CustomerIndex.html" target="_self">
                    Go to the customer home page
                </a>
            </div>
        </div>

        <script src="Scripts/<a href="http://angular.min.js">angular.min.js</a>"></script>
        <script src="Scripts/<a 
           href="http://angular-route.min.js">angular-route.min.js</a>"></script>
        <script src="Scripts/AppProduct.js"></script>
    </body>
</html>

Each of these shell views loads its own Angular module and set of view-models. The files that contain this JavaScript are called AppCustomer.js and AppProduct.js. The routing table definitions for both sections vary in their URLs of course. What is also very different from my first example is the URL path that renders the Home view, which is essentially the landing view for this SPA. In my first example, the Index.html view came up as the root of the site. In this example, I don't really have a root of the site, though I most certainly could have, if needed. Instead, the Customer section's landing view comes up if the path is “/customer”.

$routeProvider.when('/customer', {
    templateUrl: '/Templates/CustomerHome.html',
    controller: 'homeViewModel'
});

Similarly, the Product section's landing view comes up if the path is “/product”. As I explained earlier, the initial browsing point is the shell view, either CustomerIndex.html or ProductIndex.html. It's not until one of these pages loads that Angular can take over and provide additional routing help. This lack of URL friendliness is one of the problems I'm going to address later when I introduce ASP.NET MVC into the mix. By browsing to the CustomerIndex.html page, Angular bootstraps and the modules and view-models are defined, along with the routing table. And, as in my first example, the original URL, {host}/CustomerIndex.html, is remembered by Angular. This means that when the page loads and the ng-view is encountered, Angular looks at the routing table to see what view/view-model needs to be loaded into that placeholder. Because no route actually matches the CustomerIndex.html path, the “otherwise” clause is hit and the route switches over to “/customer”, loading the CustomerHome.html template into the placeholder as well as replacing the URL displaying in the browser bar.

A rather negative side effect of my current routing definition is that if I load up CustomerIndex.html, letting it go through the process I described above, and then clicked “refresh,” I'm presented with a 404 error. This is because Angular has changed the URL in the browser bar to the one that matches the route it was able to process, in this case “/customer”. Hitting the refresh button causes a complete reload of that URL from the server. Because we're not using ASP.NET, there's nothing to handle the route “/customer” and so a 404 is returned. This can be fixed by adding a route to the routing definition.

$routeProvider.when('/CustomerIndex.html', {
    templateUrl: '/Templates/CustomerHome.html',
    controller: 'homeViewModel'
});

Although this is an acceptable solution, because this route is now found by Angular, it's the route that displays in the browser bar. You can decide if this is good or bad. It certainly isn't a pretty URL, and it is, in fact, one of the problems I'll address with ASP.NET later.

As in my first example, you can click on links that are configured to a route in their href attribute, and that route will be parsed by Angular. However, to jump sections and go out of the current SPA is a different story. Traditionally, an href route is handled by Angular and the proper view template and view-model is loaded and displayed in the shell view's ng-view placeholder. If I'm sitting somewhere in the Customer section and want to jump to somewhere in the Product section, I have to handle things a little differently. My goal now becomes trying to route over to {host}/ProductIndex.html from somewhere in the Customer section. Although it may seem that you can easily do this by simply adding another route to the Customer's routing table, it can get a little complicated. Because I'm sitting in the Customer section, it's the appCustomer Angular module that's currently loaded, and thus its view-models. Adding a route definition for /ProductIndex.html tells Angular that a view template and view-model needs to be loaded from the current module and the view placed in the CustomerIndex.html's ng-view placeholder. Now you see the problem. These two sections should be treated separately, as indeed, they're separate SPAs.

If you look back at Listing 5, you'll notice that the anchor-tags for the links differ a little. The first two are customer-section-based and are routed easily by the current routing table, as defined in the appCustomer Angular module. The other two links are designed to jump to the Product section by loading ProductIndex.html. What makes the first link unique is the target attribute.

<a href="/ProductIndex.html" target="_self">
   Go to the product home page
</a>

Without it, that route is handled by the currently loaded Angular routing table and will, of course, fail. Proactively specifying a target causes the route to execute a full page-load from the server, just as if you altered the browser bar yourself and pressed enter. This starts the process from the very beginning, but loads up the ProductIndex.html page instead, thereby loading its Angular module, defining its own routing table, and defining its own view-models.

The second link to the Product section demonstrates how to accomplish this from the view-model. There are many times that you need to execute code before navigating somewhere. For these scenarios, you can simply tell the link to call a function in the view-model. In this case, I'm calling a function called ProductHome. This function is defined in the customerIndexViewModel like this:

appCustomerModule.controller("customerIndexViewModel",
 function ($scope, $http, $location, $window) {
    $scope.headingCaption = 'Customer Maintenance';
    $scope.ProductHome = function () {
        // assume it's a route in the current SPA
        //$location.path('/ProductIndex.html');
        // perform a full page-load
        $window.location.href = '/ProductIndex.html';
    }
});

The two techniques you see are the equivalent of an anchor tag with or without a specified target attribute. The one that's commented out looks for the route within the current SPA and is handled by the currently loaded routing table. This is the procedural equivalent to a standard <a> link WITHOUT a target attribute. The second one, which is the active code, performs a full page-load. Notice that each of these depends on an Angular service, $location or $window, which I inject into the view-model.

A nice benefit of where I decided to place the ProductHome function is that it's available to all the view-models loaded by the appCustomer module. This is because the customerIndexViewModel is the view-model bound to the shell view and logically sits above the ng-view directive, thus sitting above any view-models bound to views placed in the placeholder. View-models bound to views that render in the ng-view placeholder are considered nested view-models and have access to $scope variables set by the view-model bound to the shell view. This is the case for anything I place in the $scope variable in this view-model.

The rest of the appCustomer.js file is the same as I showed you in Listing 4, when I discussed the single-shell app. It consists of the appCustomer Angular module with its routing definitions, as well as all the same view-models that are in the single-shell-layout app. The rest of the Product section is set up very similarly to the Customer section. It consists of the shell view that you've already seen, as well as the appProduct.js file that contains the appProduct Angular module, routing definitions, and all view-models. The sidebar shows you where you can download the complete set of projects.

View-models bound to views that render in the ng-view placeholder are considered nested view-models and have access to $scope variables set by the view-model bound to the shell view

Although the multi-shell approach I've taken here is quite acceptable, notice that the two shell views do have some repetitive code. The navigation links and ng-view placeholder are reversed in position and the rest of the view is exactly the same. The header is the same, as is the loading of the scripts. The exception is the AppCustomer.js versus AppProduct.js scripts that load appropriately for each view. Common code can be extended to become a full header and footer, site-map information, or anything you need to give your site a polished production look-and-feel. In such a case, all that common markup needs to be duplicated in each shell view. Although I'm certain that there are products or frameworks out there to help you accomplish this, you're reading this because you're an ASP.NET developer, so why don't I show you how to leverage what you already know and accomplish this easily?

The ASP.NET MVC/Angular Hybrid App

I know I've said this previously, but I still can't help but be amazed at how many developers see AngularJS applications and ASP.NET MVC applications as two different things. Yes, they are two different technologies, but I think there is too much of an “our way or their way” mentality in Web development. Our way, meaning with the Microsoft stack, and their way meaning without. Coming from a Web Forms background, it took me a while to get on the MVC bandwagon, but once I did, I never looked back. Having said that, I rarely develop an application in the conventional ASP.NET MVC fashion. By conventional, I mean using MVC controllers for Gets and Posts, and using Razor with HTML Helpers for all of my form rendering. This was great when it came out, but we have so many better ways to do it now! Again, I never looked back. ASP.NET MVC remains a fantastic delivery platform for HTML, and JavaScript for that matter. That statement is key in my integration of MVC and Angular, because it's in that capacity that I will concentrate next.

Overview

The overall architecture of this Hybrid App, as I'm calling it, is quite simple and is displayed in Figure 1 and Figure 2. The Web application itself is, of course, an ASP.NET MVC site and renders one or more views using an MVC controller and action as any other MVC app. Primary site navigation is defined in the Layout page and uses the MVC routing engine to route to other MVC views. These views constitute the root of each site section. Note that previously, a site section was an entire HTML page, each with repeated static content. The root view of each section is considered the root of a SPA and renders everything that SPA needs, and provide the layout for that SPA. In the previous examples, I referred to these views as shell views. This layout is rendered within the layout defined for the site as a whole in the MVC Layout page. This is illustrated in Figure 1. Once in a site section, we're inside a SPA and, for the most part, everything is the same as the previous SPA examples I've shown you. I'll go through the order of operation starting with what is now the primary shell of the entire Web application, the Layout page.

Figure 1: Architecture of the Web Application
Figure 1: Architecture of the Web Application

The Layout Page

In the previous example, I demonstrated how to set up an application with two sections, each being its own SPA and each having its separate Angular module, views, and view-models. However, you might recall that I was forced to repeat some code in each of the shell views. Now I'll work in an ASP.NET MVC application with Web API services. This is a standard choice when creating a Web project in Visual Studio. My project will also have the AngularJS and Bootstrap NuGet packages installed.

I won't be making any adjustments to the Global.asax or the Web.config in the interest of simplicity, but if you use my techniques, you're free to add anything you need to your Web application, including a DI container, custom filters, or anything else. As in the prior example, this site will have a Customer section and a Product section. The code download that accompanies this article also includes an Order section but in the interest of space, I won't be covering it in the article.

The shell views, as I described them before, still exist, but as CSHTML views. These views start by sharing a common Layout page. This pattern isn't new and is core to any ASP.NET MVC application. The Layout page sets up the common look and feel for the entire application. This means that it needs to define the static content that resides outside of each SPA section. The Layout page in its entirety can be seen in Listing 7.

Listing 7: The ASP.NET MVC Layout View

<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8" />
        <meta name="viewport" 
              content="width=device-width, initial-scale=1.0">
        <title>@ViewBag.Title - ASP.NET MVC with Angular JS</title>
        @Styles.Render("~/Content/css")
        @Scripts.Render("~/bundles/modernizr")
    </head>
    <body data-ng-app="main">
        <div class="navbar navbar-inverse navbar-fixed-top">
            <div class="container">
                <div class="navbar-header">
                    <button type="button" class="navbar-toggle" 
                            data-toggle="collapse" 
                            data-target=".navbar-collapse">
                        <span class="icon-bar"></span>
                        <span class="icon-bar"></span>
                        <span class="icon-bar"></span>
                    </button>
                    @Html.ActionLink("ASP.NET MVC/Angular JS", 
                        "Index", "Home",
                        new { area = "" }, 
                        new { @class = "navbar-brand" })
                </div>
                <div class="navbar-collapse collapse">
                    <ul class="nav navbar-nav">
                        <li>@Html.ActionLink("Customers",
                                             "Customer",
                                             "Home")</li>
                        <li>@Html.ActionLink("Products", 
                                             "Product", 
                                             "Home")</li>
                        <li>@Html.ActionLink("Orders",
                                             "Order",
                                             "Home")</li>
                    </ul>
                </div>
            </div>
        </div>
        <div class="container body-content">
            @RenderBody()
            <hr />
            <footer>
                <p>&copy; @DateTime.Now.Year</p>
            </footer>
        </div>

        @Scripts.Render("~/bundles/angular")
        @Scripts.Render("~/bundles/jquery")
        @Scripts.Render("~/bundles/bootstrap")
        <script type="text/javascript">
            window.MyApp = {};
            MyApp.rootPath = '@Url.Content("~")';
        </script>
        <script src="~/App/Validator.js"></script>
        <script src="~/App/App.js"></script>
        @RenderSection("scripts", required: false)
        <script type="text/javascript">
            @RenderSection("jsCode", required: false)
        </script>
    </body>
</html>

I refer to each SPA section as a SPA Silo, a term coined by my friend, author Brian Noyes. Navigation links on the Layout page route to a conventional MVC controller action, as in any other ASP.NET MVC application.

<ul class="nav navbar-nav">
   <li>
      @Html.ActionLink("Customers",
                       "Customer", "Home")
   </li>
   <li>
      @Html.ActionLink("Products",
                       "Product", "Home")
   </li>
</ul>

The body of the Layout page can look however you want and contain any static content you need. The placeholder for the actual SPA Silo that makes up the section of the site is a standard ASP.NET MVC RenderBody statement.

<div class="container body-content">
    @RenderBody()
    <hr />
    <footer>
        <p>&copy; @DateTime.Now.Year</p>
    </footer>
</div>

The bottom of the Layout page adds any scripts that are to be common to the entire application. This is one of the items that I needed to repeat in each shell view in the previous example. Here, I only need to add it once in the Layout view. What I won't be adding in this view are the Angular modules and view-models used by each SPA Silo.

@Scripts.Render("~/bundles/angular")
@Scripts.Render("~/bundles/jquery")
Scripts.Render("~/bundles/bootstrap")

Next, I want a set of a global variables that contains the root URL to this application. This makes it easy to deploy anywhere later without worrying about what the root address is. This problem rears its ugly head quite a bit when you're developing applications on your localhost using a virtual directory, and then deploy to an actual website where your applications sit at the root level. In many situations, you may need to refer to the root path of the application and having this variable handy makes that much easier.

<script type="text/javascript">
    window.MyApp = {};
    MyApp.rootPath = '@Url.Content("~")';
</script>

The root path of the application is easily obtained at the server, so combining Razor syntax here with JavaScript will work nicely. Remember, this view is being parsed and rendered by ASP.NET, so I'm free to include and leverage any server-side power that I want. The creation of the MyApp namespace is so not to pollute JavaScript's global namespace and avoid any potential conflict - although the possibility of a conflict is minimal.

Now I need to load up any additional scripts that the entire site will use.

<script src="~/App/App.js"></script>

The App.js file contains JavaScript code that's available and leveraged to the entire application, meaning all SPA Silos. I'll go over its design and contents in detail later. Notice that this file resides in the App folder. This folder contains all of the Angular-related code for the entire application. You'll see other App.js files in this same Web application later, but their locations will be very different.

The last two parts of the Layout page are code section definitions. This is a feature that's been used by traditional MVC applications to render HTML code-parts but can also be used for scripts. I've added two sections, both non-required.

@RenderSection("scripts", required: false)
<script type="text/javascript">
    @RenderSection("jsCode", required: false)
</script>

The first, called scripts, will be used by the view that gets rendered by the RenderBody statement to contain any <script> tags needed for that SPA Silo. Remember, this view constitutes the Root View of that SPA Silo, similar to the shell views in the previous example, but one level beneath the primary Layout page, which is now the site's main shell. The second section is called jsCode and resides inside <script> tags, so the code contained in that section is raw JavaScript. Later, when views are rendered by the RenderBody statement, whatever is placed in the scripts and jsCode sections is rendered in these placeholders of the Layout view.

If you look at the complete Layout page code in Listing 7, you may notice that I'm bootstrapping an Angular module called main in the <body> tag.

<body data-ng-app="main">

This module is the only Angular module bootstrapped declaratively. Angular doesn't allow more than one module to be assigned using data-ng-app, but it doesn't mean that only one module is allowed. Later, you'll see that this is the reason for the aforementioned jsCode section of the Layout page. Also, if I wanted, I could also assign an Angular view-model to the Layout page using the data-ng-controller directive. This gives my Layout page the ability to benefit from binding to $scope variables like any other Angular view. I've chosen, however, to keep the Layout page simple and limit it to providing the static shell layout design for the site and a place to put site-section navigation links, as I've already shown you. This does bring me to the main Angular module. Because this module is assigned here in the Layout page, the script file that contains its definition is here as well. This is the App.js file you saw earlier.

The Site-Wide App.js and Index.cshtml View

Following the regular ASP.NET MVC order of operations, running my site would use the default MVC route {controller}/{action}/{id}, and thus seek out a controller class called HomeController and an action called Index. It's entirely up to me to maintain this pattern or change it to a different default route or a different default view. To keep things simple, I'm maintaining the default pattern and going with a default view called Index.cshtml.

The App.js file I recently mentioned is used to contain code used by the entire site, hence its declaration in the Layout page. The location of this file is a folder called App. This is the pattern I've chosen for my MVC application. The App folder itself contains this JavaScript file and any others that are used by the site. Later, you'll see the sub-folder structure I'll add in the App folder. This file declares two modules that are very important. The first Angular module declared is called common.

var commonModule = 
    angular.module('common', ['ngRoute']);

This module won't be attached to any view directly. I use it as a place to hang shared services from and also as a place to inherit other modules from, as in this case, ngRoute. You might have noticed the ngRoute module in previous code examples. It's declared in the Angular script and is necessary for routing to function properly. Adding other Angular services to my site will become easier now, because I just need to add them to the inheritance array in the common module declaration. The App.js file also declares a shared service called viewModelHelper. Its contents are beyond the scope of this article, but it's included in its entirety with the accompanying code download. I declare the viewModelHelper service by using the factory method from the common module.

commonModule.factory('viewModelHelper',
    function ($http, $q, $window, $location) {
        return MyApp.viewModelHelper(
            $http, $q, $window, $location);
    });

Then, I simply create the viewModelHelper function.

(function (myApp) {
    var viewModelHelper = function (
        $http, $q, $window, $location) {

        var self = this;

        // function members go here
        
        return this;
    };
    myApp.viewModelHelper = viewModelHelper;
}(window.MyApp));

Notice that I'm using the myApp namespace I declared back in the Layout page. As I stated earlier, this is to avoid polluting the global namespace.

Having the common module makes it easy for the main module and all other Angular modules going forward to inherit functionality that I need across the entire site, because all they have to inherit from in order to gain such functionality is the common module. The declaration of the main module does just that.

var mainModule = angular.module(
   'main', ['common']);

This module is attached to the Layout page's <body> tag, as you saw earlier, and because it inherits from the common module, it also inherits from the ngRoute module and the declaration of the viewModelHelper shared service. Using this service in a controller now is as easy as injecting it by using its name. Note that, as noted in the above syntax, the name of the module as attached to the Layout page is main, but the variable that represents the module for procedural code is mainModule.

The Index.cshtml view that comes up by default (based on the default MVC route) when the site runs is bound to an Angular view-model called indexViewModel. This view-model is also declared in this App.js file and it hangs off of the main module.

mainModule.controller("indexViewModel", 
   function (
    $scope, $http, $q, $routeParams, $window,
    $location, viewModelHelper) {
    var self = this;
    $scope.topic =
        "Integrating ASP.NET MVC and AngularJS";
    $scope.author = "Miguel A. Castro";
});

In the case of this view, the currently bootstrapped Angular module is main because it was attached in the Layout page. The services I'm injecting into this view-model are the common ones that I inject into all my view-models, although I'm not really using any of them here except $scope. Notice the injection of the viewModelHelper service as well. The Index.cshtml view can be seen in Listing 8, where you can see me use the topic and author variables as they are declared in the view-model.

Listing 8: Index.cshtml

@{
    ViewBag.Title = "Integrating ASP.NET MVC and AngularJS";
}
<div data-ng-controller ="indexViewModel">
    <div class="jumbotron">
        <h1>{{ topic }}</h1>
        <h2>Author: <b>{{ author }}</b></h2>
        <p class="lead">This site brings together two great
                        platforms for web development.</p>
    </div>

    <div class="row">
        <div class="col-md-8">
            <p>AngularJS is the leading JavaScript-based client
               framework for web development.</p>
            <p>ASP.NET MVC remains Microsoft's prefered web
               development framework and HTML delivery platform</p>
            <p>
                When combined, these two platforms can build rich
                web applications while maintaning great code
                manageability as well as offer great
                server-side support.
            </p>
        </div>
    </div>
</div>

Clicking on the site heading of the Layout page routes you to the Index.cshtml view from anywhere in the site in the conventional MVC fashion. Clicking on any of the links for customer or Product calls upon the Home Controller's customer or Product action methods. These actions are designed to render the Customer.cshtml or Product.cshtml MVC views respectively. These views are the root views for the Customer and Product SPA Silos.

The SPA Root Views

The standard MVC route {controller}/{action}/{id} allows me to route to Home/Customer or Home/Product and get to the Customer.cshtml and Product.cshtml views. Later, I'll alter these routes, but for now, I'll continue based on the fact that it was the default route table entry that got me to these views. For the purposes of the example going forward, I'll stick to the Customer section, but the Product section will be set up exactly the same way.

The Customer.cshtml view is the “root” of the Customer SPA Silo. The primary goal for this view is similar to the shell views in the first examples I went over when describing more traditional SPAs. It sets up the layout for this section of the website and loads up any necessary scripts. The main difference between this and the shell views is that this is the root of the Customer section and it is, in its entirety, rendered inside the Layout page by the RenderBody statement. This is where you see a huge benefit of using ASP.NET MVC as the primary delivery mechanism. I can have site-wide layout and site-wide scripts, and also section-wide layouts and section-wide scripts. For this difference, I refer to Customer.cshtml as a root view.

Because this view is the root view for a SPA Silo, the customer SPA Silo, it uses a separate Angular module along with its own set of Angular view-models. The JavaScript files that contain all of this information are loaded in the scripts section of this view. Remember, this view is loaded by ASP.NET MVC, meaning that I have all the power of the server-side Razor engine at my disposal. The scripts section is combined with the Layout page where the section was defined.

@section scripts {
    <script src="~/App/Customer/App.js">
          </script>
    <script src="~/App/Customer/ViewModels/
                RootViewModel.js">
          </script>
    <script src="~/App/Customer/ViewModels/
                 CustomerHomeViewModel.js">
          </script>
    <script src="~/App/Customer/ViewModels/
                 CustomerListViewModel.js">
          </script>
    <script src="~/App/Customer/ViewModels/
                CustomerViewModel.js">
          </script>
}

You may recall me mentioning that there will be other App.js files later. You can see one here, but notice its location. This is key to the naming convention I use. Because I declared this to be the Customer section of my site, I have a customer folder under the App folder. The App.js file in this folder accommodates everything for the Customer section only, whereas the App.js that is one level up in the App folder accommodates all sections.

The customer folder under the App folder is an example of the folder structure I use throughout my entire site. The sub-folders immediately beneath the App folder each represent a site section. I use the same name as the action method in the HomeController MVC controller class that navigated me to this section. Under these sub-folders, I have the SPA Silo's App.js file, which I've just described. I also have sub-folders for everything else that deals with this particular SPA Silo. These sub-folders include, but are not limited, to a ViewModels folder that contains the Angular view-models for this SPA Silo, and a Views folder that contains the HTML templates. The files in these two folders have a one-to-one correspondence with one another and their naming differs only in their suffixes, ViewModel and View. Additional sub-folders can include scripts and content as well. Given this convention, my site's App folder looks like Figure 3.

Figure 2: Structure of a SPA Silo
Figure 2: Structure of a SPA Silo

In reference to the recent code-snippet, where I show you the “scripts” section, notice that I'm loading four view-models. The one declared in the RootViewModel.js file is the view-model that applies to this root view.

Figure 3: Hybrid App Folder Structure
Figure 3: Hybrid App Folder Structure

This view needs to bind to its own Angular module. The reason for this is primarily to isolate the view-models, services, and routes that will be handled within this section. It also makes things much more manageable. The module, to be called customer, cannot however be attached using data-ng-app. That directive can only be used once per current rendering, and if you recall, I used it in the <body> tag of the Layout page to attach the main module. I can, however, load and attach it procedurally, and that is what the jsCode script section is for.

@section jsCode {
  angular.bootstrap(
    document.getElementById("customer"),
    ['customer']);
}

Here, I'm telling Angular to attach the customer module (in the square brackets) to the element with the name customer (in the parentheses).

The body of the view is wrapped in a div tag marked with the Angular ng-non-bindable directive.

<div ng-non-bindable>

By definition, this tells Angular to ignore the contents of the div tag. In reality, what it's telling it is to ignore what's been set up for Angular before this. I'm referring to the main module that was bootstrapped by the Layout page. This allows another module to be attached without confusing the system. I'll identify the next div tag down with the name customer, performing the procedural bootstrapping that I showed you earlier.

<div id="customer" 
     data-ng-controller="rootViewModel">

I'm also binding this tag and all its content to the rootViewModel Angular view-model.

The rest of the view is made of HTML markup in combination with Angular directives and bindings. It draws the layout for the Customer section and provides any state and behavior that will be used throughout the entire section. The area to be used to swap inner views around is handled, of course, by the ng-view directive. The body of this view in its entirety can be seen in Listing 9.

Listing 9: Body of the Customer.cshtml View

<div ng-non-bindable>
    <div id="customer" data-ng-controller="rootViewModel">
        <h2>{{ pageHeading }}</h2>
        <div class="row">
            <div class="col-md-4">
                <button class="btn btn-primary"
                        ng-click="customerList()">
                    Customer List
                </button>
            </div>
            <div class="col-md-4">
                <button class="btn btn-primary"
                        ng-click="showCustomer()">
                    Customer
                </button>&nbsp;
                <input type="text"
                       ng-model="customerService.customerId" />
            </div>
        </div>
        <hr />
        <div ng-view></div>
        <hr />
        <div>
            <a href="/product" target="_self">Products</a>
        </div>
    </div>
</div>

Notice the link to take me to the Products section. It has the additional attribute target, as I described earlier in this article. This forces the link to go to the server, thus turning the process back over to ASP.NET MVC. Later I'll explain why this route is /product and not the /home/product that the default MVC route table entry requires. Everything in the body of this view is bindable to any $scope exposed by the rootViewModel Angular view-model that's declared in the App.js file located in the App/Customer folder. The contents of this file, in conjunction with the other view-models loaded by this root view, along with the Customer.cshtml root view itself, make up the Customer SPA Silo as illustrated back in Figure 2.

A SPA Silo

The Customer SPA Silo is where a user finds anything to do with managing customer information. The root view, Customer.cshtml, designs the layout and provides the ng-view placeholder for every sub-view to be contained within. The rootViewModel Angular view-model is bound to the root view, as previously described, and that view-model is created from the customer Angular module, also as previously described. The customer module is the first construct contained in the Customer section's App.js file. This module inherits from the aforementioned common module.

var customerModule = 
    angular.module('customer', ['common']);

This module also defines the routing tables to be used by the Customer SPA Silo. Each route results in a view and view-model combination to be placed in the root view's ng-view placeholder.

customerModule.config(function
     ($routeProvider,
      $locationProvider) {
    $routeProvider.when('/customer', {
        templateUrl: '/App/Customer/Views/
                      CustomerHomeView.html',
        controller: 'customerHomeViewModel'
    });
    $routeProvider.when('/customer/list', {
        templateUrl: '/App/Customer/Views/
                      CustomerListView.html',
        controller: 'customerListViewModel'
    });
    $routeProvider.when('/customer/show/
                              :customerId', {
        templateUrl: '/App/Customer/Views/
                      CustomerView.html',
        controller: 'customerViewModel'
    });
    $routeProvider.otherwise({
        redirectTo: '/customer'
    });
    $locationProvider.html5Mode({
        enabled: true,
        requireBase: false
    });
});

Notice that each route starts with the path /customer. Also, notice the physical path to the view templates, /App/Customer/Views. If you recall back in the root view's scripts section, the location of the view-model files was /App/Customer/ViewModels. In my environment, I've been running this project using IIS Express right in Visual Studio. I've found that if I set it up to run in full-blown IIS, I sometimes run into that problem I previously described where it can't find certain files when the path merely starts with /. This has to do with how I set up the application to run in IIS, be it in an application or a virtual directory. To avoid this and any other confusion that may occur during deployment, I can preface the URL path as well as the templateUrl path with MyApp.rootPath.

$routeProvider.when(MyApp.rootPath +
    '/customer', {
    templateUrl: MyApp.rootPath +
    '/App/Customer/Views/
     CustomerHomeView.html',
    controller: 'customerHomeViewModel'
});

Now, no matter what my execution location is, I also have the correct URL path.

The Customer Silo's App.js file also contains the declaration of the customerService Angular service. This service can be used to share state and behavior among all the view-models bound to views that get placed in the ng-view placeholder.

customerModule.factory('customerService',
    function ($http, $location,
              viewModelHelper) {
        return MyApp.customerService($http,
            $location, viewModelHelper);
    });
(function (myApp) {
    var customerService = function ($http,
        $location,
        viewModelHelper) {
            var self = this;
            // shared state and behavior can go here
        return this;
        };
        myApp.customerService = customerService;
}(window.MyApp));

The rootViewModel view-model that's loaded and bound to the Customer.cshtml root view is also contained in the App/Customer folder under the ViewModels sub-folder. Listing 10 shows this view-model in its entirety. I declared it from the customer module, depicted by the customerModule variable, and am injecting into it not only some standard Angular services, but also the viewModelHelper service declared in the application-centric App.js file and the customerService declared in the customer-section-centric App.js file.

Listing 10: Customer SPA Silo's rootViewModel

customerModule.controller("rootViewModel",
    function ($scope, customerService, $http, viewModelHelper) {

    $scope.viewModelHelper = viewModelHelper;
    $scope.customerService = customerService;

    $scope.flags = { shownFromList: false };

    var initialize = function () {
        $scope.pageHeading = "Customer Section";
    }

    $scope.customerList = function () {
        viewModelHelper.navigateTo('customer/list');
    }

    $scope.showCustomer = function () {
        if (customerService.customerId != 0) {
            $scope.flags.shownFromList = false;
            viewModelHelper.navigateTo(
                'customer/show/' + customerService.customerId);
        }
    }

    initialize();
});

The contents of this view-model are specific to the Customer.cshtml root view, so if you look again at Listing 9, you can reconcile the customerList() and showCustomer() function calls with the functions declared in Listing 10. The incoming services, viewModelHelper and customerService are given $scope assignments so that their contents can be used in bindings by the view.

    $scope.viewModelHelper = viewModelHelper;
    $scope.customerService = customerService;

Also, remember that the rootViewModel's $scope contents are accessible by any view-models bound to views that get placed in the ng-view placeholder. I described this nested view-model pattern earlier when I discussed traditional SPAs. I'm not going to get into the implementation details of what each of my view-models shows or does, as it's all standard Angular stuff that was a prerequisite for this article. I will, however, bring attention to an example of how I'm using the viewModelHelper service by calling the navigateTo method in order to route me to another view/view-model within this SPA Silo.

viewModelHelper.navigateTo('customer/list');

This method encapsulates a common way I call upon Angular's $location.path command to perform the loading of a new view.

self.navigateTo = function (path, params) {
    if (params == null)
        $location.path(MyApp.rootPath + path);
    else
        $location.path(MyApp.rootPath + path).search(params);
}

This is how all methods or variables are declared in the viewModelHelper service, or any other shared service for that matter. If that service is injected into a view-model, as it in the case of rootViewModel, all of these members can be accessed.

When the Customer.cshtml root view is rendered, it loads up the customer module and the rootViewModel view-model, bootstraps the module to the view, and binds the view-model to the div tag with the data-ng-controller directive. When the customer module is declared and its routing table configured, Angular recalls the original URL that got it here to begin with. Remember, sticking to the standard ASP MVC default route pattern, this was /Home/Customer. Now that the Angular module has loaded and an ng-view directive has been found in the Customer.cshtml root view, Angular will attempt to take that route and run it through the routing table that I declared into the customer module. If you look back at the code-snippet above that where I showed the routing configuration, you'll see that the route /Home/Customer isn't in the list. This means that the otherwise clause will be hit and the route will be changed to /customer. Again, Angular runs this through the list of configured routes and this time it finds it. The result is that it loads the Home.html view from the App/Customer/Views folder and binds it to the customerHomeViewModel Angular view-model. This view-model was declared when the CustomerHomeViewModel.js script was loaded in the scripts MVC section in the Customer.cshtml root view.

customerModule.controller(
    "customerHomeViewModel",
    function ($scope, customerService,
              viewModelHelper) {
    $scope.viewModelHelper = viewModelHelper;
    $scope.customerService = customerService;
    var initialize = function () {
    }
    initialize();
});

The initialize pattern that you see is my standard for doing things in all of my view-models. The view to which this view-model gets bound is very simple, as it doesn't contain anything special and neither does its view-model.

<div>
    <h3>Select an option using one of the
        Customer buttons above</h3>
</div>

It's very important to point out that, unlike the Customer.cshtml root view, you don't see any data-ng-controller directive here. This is because the connection between the view and the view-model is done by the routing table entry itself.

    $routeProvider.when('/customer', {
        templateUrl: '/App/Customer/Views/
                            CustomerHomeView.html',
        controller: 'customerHomeViewModel'
    });

An alternative is to add a data-ng-controller directive to the div tag at the top of the CustomerHomeView.html view, but I need to completely omit it from the routing definition. If I have both, the view-model loads twice and any code that's immediately executed, such as the initialize function, will be executed twice. Going forward, any functions called by HTML elements in the view will also be called twice, and, depending on what that function does, this can be dangerous for the application. It's also very bad practice to simply “let it go.” Although I can choose either of the two methods of attaching the view-model to the view, I prefer to omit the directive from the view and declare it in the routing definition so it can be seen by the developer immediately.

This now becomes the view and view-model combination that gets placed into the ng-view placeholder of the Customer.cshtml root view upon first rendering this SPA Silo. A byproduct of this route shuffle is that the URL in the browser bar now changes to {host}/customer. This can be a little misleading because the link that got me to the Customer.cshtml view to begin with was {host}/home/customer. I'll show you how I plan to remedy this and more later, when I cover routing specifics.

Contained views inside an ng-view should either bind their view-model through data-ng-controller or in the routing definition, but not both.

From this point on, it's all about adding views and view-models and providing navigation to them though links or view-model functions. If you look back at the code for Customer.cshtml, you'll see that I declared two buttons. One was to show the customer list and the other was a specific customer, based on a bound value to a textbox.

<div class="col-md-4">
    <button class="btn btn-primary" 
            ng-click="customerList()">
        Customer List
    </button>
</div>
<div class="col-md-4">
    <button class="btn btn-primary" 
            ng-click="showCustomer()">
        Customer
    </button>&nbsp;
    <input type="text"
           ng-model="customerService.
                     customerId" />
</div>

The two functions in the rootViewModel view-model navigate to one of the other routes declared by the routing configuration of the customer module in the SPA Silo's App.js file.

$scope.customerList = function () {
    viewModelHelper.navigateTo('customer/list');
}
$scope.showCustomer = function () {
    if (customerService.customerId != 0) {
        $scope.flags.shownFromList = false;
        viewModelHelper.navigateTo(
            'customer/show/' + 
             customerService.customerId);
    }
}

I chose to perform both navigations procedurally in the interest of consistency and the possibility of enhancing the functions further in the future. However, the link to display the customer list could have been handled entirely by the <a> tag instead of leveraging an ng-click directive.

The result of each navigation attempt is another view/view-model combination placed in the ng-view placeholder of the Customer.cshtml root view. If such an attempt fails because the route isn't found, the otherwise clause is invoked and the /customer route is handled.

It's important to understand the distinction of which routing engine handles what. The routing I've just described is handled by the Angular routing table, and each SPA Silo (in this case, the Customer SPA Silo) is in charge of defining and handling its own set of routes. The procedural way of performing this kind of routing is through the path method of the $location Angular service, which I wrapped in my viewModelHelper shared service. An anchor tag with a target="_self" attribute on it will hit the server. If the server is hit, ASP.NET handles the request first and the MVC routing engine is engaged first. This brings me back to the link in the Customer.cshtml root view that contained the route “/product”.

<a href="/product" target="_self">Products</a>

This link hits the server and thus invokes the ASP MVC routing engine. Remember so far, I've only been using the default route entry of {controller}/{action}/{id}, meaning that the route should say /Home/Product/ or it will fail. Also, what if I wanted to cross from this SPA Silo, not to the root view of the Product SPA Silo, but directly to the list of products. Ideally this should be handled by a link that jumps to the URL “/product/list”.

<a href="/product/list" target="_self">Product List</a>

In my current configuration, not only would the “/product” link fail if I'm in the Customer SPA Silo, but so will the “/product/list” route. Both of these routes are defined in the product module of the App.js located in the /App/Product folder. Making the ASP.NET MVC routing engine and Angular routing engine play nicely together isn't difficult but it requires some adjustment on my part?or yours.

Dual Routing Engines

No website is production quality without proper route validity and integrity. This means that once a route is visibly made available to a user, it should be available from any part of the site. Today, you must consider the fact that users write URLs down to use later, or even more frequently, bookmark things. They also email and text URLs to others, so it's up to us as developers to ensure that any route they learn works with direct navigation as well as with a browser refresh. Take, for example, the route "/product/list". From what you've learned so far, this is a route handled by the Angular routing configuration assigned in the product module of the Product.cshtml root view. But the problem is that the "/product/list" route means nothing to ASP.NET MVC. Again, from what you've seen so far, in order to get to the Product SPA Silo, you need to route to "home/product" so you can hit the Home MVC controller and Product action. As it stands right now, a link in a customer view that looks like this:

<a href="/product/list" target="_self">Product List</a>

will error out for two reasons. First, this route is not defined in the Angular route definition of the customer module. And it cannot be added because the view and, more specifically, the view-model that it needs, don't belong to the customer module and haven't even been script-loaded. Secondly, the route isn't specifically defined in the MVC routing table either. In fact, it would have been interpreted as needing to hit a controller class called ProductController and an action method called List; neither of which exist.

Fixing this is easy and is done in the RouteConfig class in the Web application's App_Start folder. This is where the default MVC route is declared.

routes.MapRoute(
    name: "Default",
    url: "{controller}/{action}/{id}",
    defaults: new {
        controller = "Home",
        action = "Index",
        id = UrlParameter.Optional }
);

This is where I need to make an addition for each SPA Silo. If you recall, the Home controller contains two actions in addition to the Index action, customer and Product. I'm going to add a route that looks like this:

routes.MapRoute(
    name: "customer",
    url: "customer/{*catch-all}",
    defaults: new {
        controller = "Home", 
        action = "Customer" });

This route will be used by any URL that starts with customer, and routes it to the customer action in the HomeController controller class, just as if the route had said Home/Customer. The term catch-all can be anything you want, as long as it has the asterisk (*) in front of it. This route allows me to call the URL customer/list right from the browser bar. The result is the Customer.cshtml root view, which is what's called from the HomeController class' customer action. But, as I've stated several times, Angular remembers the original route called. Because getting to the Customer root view loads up the Angular module and Customer SPA Silo's routing configuration, it then takes this original URL of customer/list and routes it through the Angular routing engine. When it does this, it finds the route and renders the proper view and view-model in the ng-view placeholder of the Customer.cshtml root view.

Each SPA Silo needs a similar MVC route entered into the routing table.

routes.MapRoute(
    name: "product",
    url: "product/{*catch-all}",
    defaults: new {
        controller = "Home", 
        action = "Product" });

Now, the /product/list link that I showed you earlier will work just fine and route to the Product.cshtml root view first, loading the product module and its routing configuration, and eventually finding the route and rendering the ProductListView.html template as well as binding it to the productListViewModel view-model. Just don't forget that if a link like this is outside of the Product SPA Silo, it must include the target="_self" attribute. If it's within the Product SPA Silo, it shouldn't have that attribute. In fact, placing this attribute on a link in a view in the very SPA Silo to where you want the link to route causes an unnecessary server trip. Not only that, by initiating a server trip, any state you were holding in a shared service such as a productService Angular service will be lost because the HTTP request-response process starts from the top again.

Of course, it's up to you to account for any page-order that you need to enforce in your application. This means that if a route gets a user to a specific view but there was a business prerequisite before that view is properly used, you need to place logic in the view-model to either route-away or signal other instructions to the user.

Conclusion

So there you have it, the worlds of ASP.NET MVC and Angular JS working together, not only in harmony but benefiting from each other's strengths. You see no jamming square pegs into round holes in this scenario. Each technology is used in the manner for which it was designed and in an elegant fashion. The two technologies coexist nicely and aren't fighting each other in any way. It's truly a match made in heaven.

The techniques I've used for integrating the two technologies don't necessarily have to apply to Angular only. I've used the same design with ASP.NET MVC and Knockout, and I have no reason to believe that it wouldn't work with Ember, Backbone, or any other JavaScript framework.

Make sure that you download the code that accompanies this article from the information provided in the sidebar. It contains quite a bit more than what I was able to cover in this article, although it's all extras that aren't key to the integration techniques I've illustrated.