In a classic ASP.NET MVC application, stepping between parts of the application usually requires a trip to the web server to refresh the entire contents of the page. However, in a Single Page Application (SPA), only elements within the page are updated giving the user a better, more responsive experience. A SPA implementation may still make transitions to other sections of the application with a full page refresh, but generally a single page manages the application’s functionality through dynamic views and web service (AJAX) calls.

AngularJS supports dynamic views through routes and the ngView directive. Your MVC application would provide the partial views that the ngView directive will dynamically load. The Simple Routing recipe of the AngularJS MVC Cookbook provides an example of setting up routes and displaying dynamic views.

In this example, the views, called Home, About, and Contact, are simple partial views rendered by the MVC controller.

    public class HomeController : Controller
    {
        public ActionResult Index()
        {
            return View();
        }

        public ActionResult Home()
        {
            return PartialView();
        }

        public ActionResult About()
        {
            return PartialView();
        }

        public ActionResult Contact()
        {
            return PartialView();
        }
    }

The Index page is the “container” page that will host the dynamic views. It has the responsibility of loading the initial Angular configuration, including any required javascript files. The ngApp directive (“myApp” in this example) kicks off the Angular bootstrap process

Routes are configured on an Angular module. They specify properties of each route, including the view and controller to use:

angular
    .module('myApp', [
        'myApp.ctrl.home',
        'myApp.ctrl.contact',
        'myApp.ctrl.about'
    ])
    .config(['$routeProvider', '$locationProvider', function ($routeProvider, $locationProvider) {
        
        // Specify the three simple routes ('/', '/About', and '/Contact')
        $routeProvider.when('/', {
            templateUrl: '/Home/Home',
            controller: 'homeCtrl',
        });
        $routeProvider.when('/About', {
            templateUrl: '/Home/About',
            controller: 'aboutCtrl',
        });
        $routeProvider.when('/Contact', {
            templateUrl: '/Home/Contact',
            controller: 'contactCtrl'
        });
        $routeProvider.otherwise({
            redirectTo: '/'
        });

        // Specify HTML5 mode (using the History APIs) or HashBang syntax.
        $locationProvider.html5Mode(false).hashPrefix('!');

    }]);

Routes are used by the ngView directive which has the responsibility of loading the route’s content. As the routes change, the content gets automatically updated.

If you run the application, watch the network activity for the browser.  You’ll notice that the initial page will load, including the “Home” dynamic view.  Then switching to the About and Contact views will load these views once, but after that, no longer make trips to the server. By this, you can see how the user will get a more dynamic, responsive experience.

As I mentioned previously, AngularJS was built from the ground up with testing in mind. It’s not an afterthought. So in that spirit, let’s look at how we might test the functionality of this example.