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.

Leverage your ASP.NET MVC skills together with your Angular skills in order to have your cake and eat it too.

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.

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 tag.

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.

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.

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 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. 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