Using Angular for Single Page Applications (SPAs)

Warning

This page documents version 1.0.0-rc1 and has not yet been updated for version 1.0.0

By Venkata Koppaka and Scott Addie

In this article, you will learn how to build a SPA-style ASP.NET application using AngularJS.

View or download sample code

What is AngularJS?

AngularJS is a modern JavaScript framework from Google commonly used to work with Single Page Applications (SPAs). AngularJS is open sourced under MIT license, and the development progress of AngularJS can be followed on its GitHub repository. The library is called Angular because HTML uses angular-shaped brackets.

AngularJS is not a DOM manipulation library like jQuery, but it uses a subset of jQuery called jQLite. AngularJS is primarily based on declarative HTML attributes that you can add to your HTML tags. You can try AngularJS in your browser using the Code School website.

This article focuses on Angular 1.X with some notes on where Angular is heading with 2.0.

Getting Started

To start using AngularJS in your ASP.NET application, you must either install it as part of your project, or reference it from a content delivery network (CDN).

Installation

There are several ways to add AngularJS to your application. If you’re starting a new ASP.NET Core web application in Visual Studio, you can add AngularJS using the built-in Bower support. Simply open bower.json, and add an entry to the dependencies property:

{
  "name": "ASP.NET",
  "private": true,
  "dependencies": {
    "bootstrap": "3.3.5",
    "jquery": "2.1.4",
    "jquery-validation": "1.14.0",
    "jquery-validation-unobtrusive": "3.2.4",
    "angular": "1.5.7",
    "angular-route": "1.5.7"
  }
}

Upon saving the bower.json file, Angular will be installed in your project's wwwroot/lib folder. Additionally, it will be listed within the Dependencies/Bower folder. See the screenshot below.

Solution Explorer with Angular Project

Next, add a <script> reference to the bottom of the <body> section of your HTML page or _Layout.cshtml file, as shown here:

<environment names="Development">
    <script src="~/lib/jquery/dist/jquery.js"></script>
    <script src="~/lib/bootstrap/dist/js/bootstrap.js"></script>
    <script src="~/lib/angular/angular.js"></script>
</environment>

It's recommended that production applications utilize CDNs for common libraries like Angular. You can reference Angular from one of several CDNs, such as this one:

<environment names="Staging,Production">
    <script src="//ajax.aspnetcdn.com/ajax/jquery/jquery-2.1.4.min.js"
            asp-fallback-src="~/lib/jquery/dist/jquery.min.js"
            asp-fallback-test="window.jQuery">
    </script>
    <script src="//ajax.aspnetcdn.com/ajax/bootstrap/3.3.5/bootstrap.min.js"
            asp-fallback-src="~/lib/bootstrap/dist/js/bootstrap.min.js"
            asp-fallback-test="window.jQuery && window.jQuery.fn && window.jQuery.fn.modal">
    </script>
    <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.8/angular.min.js"
            asp-fallback-src="~/lib/angular/angular.min.js"
            asp-fallback-test="window.angular">
    </script>
    <script src="~/js/site.min.js" asp-append-version="true"></script>
</environment>

Once you have a reference to the angular.js script file, you're ready to begin using Angular in your web pages.

Key Components

AngularJS includes a number of major components, such as directives, templates, repeaters, modules, controllers, components, component router and more. Let's examine how these components work together to add behavior to your web pages.

Directives

AngularJS uses directives to extend HTML with custom attributes and elements. AngularJS directives are defined via data-ng-* or ng-* prefixes (ng is short for angular). There are two types of AngularJS directives:

  1. Primitive Directives: These are predefined by the Angular team and are part of the AngularJS framework.

  2. Custom Directives: These are custom directives that you can define.

One of the primitive directives used in all AngularJS applications is the ng-app directive, which bootstraps the AngularJS application. This directive can be applied to the <body> tag or to a child element of the body. Let's see an example in action. Assuming you're in an ASP.NET project, you can either add an HTML file to the wwwroot folder, or add a new controller action and an associated view. In this case, I've added a new Directives action method to HomeController.cs. The associated view is shown here:

@{
    Layout = "";
}
<html>
<body ng-app>
    <h1>Directives</h1>
    {{2+2}}
    <script src="~/lib/angular/angular.js"></script>
</body>
</html>

To keep these samples independent of one another, I'm not using the shared layout file. You can see that we decorated the body tag with the ng-app directive to indicate this page is an AngularJS application. The {{2+2}} is an Angular data binding expression that you will learn more about in a moment. Here is the result if you run this application:

Simple Angular Directive

Other primitive directives in AngularJS include:

ng-controller Determines which JavaScript controller is bound to which view.

ng-model Determines the model to which the values of an HTML element's properties are bound.

ng-init Used to initialize the application data in the form of an expression for the current scope.

ng-if Removes or recreates the given HTML element in the DOM based on the truthiness of the expression provided.

ng-repeat Repeats a given block of HTML over a set of data.

ng-show Shows or hides the given HTML element based on the expression provided.

For a full list of all primitive directives supported in AngularJS, please refer to the directive documentation section on the AngularJS documentation website.

Data Binding

AngularJS provides data binding support out-of-the-box using either the ng-bind directive or a data binding expression syntax such as {{expression}}. AngularJS supports two-way data binding where data from a model is kept in synchronization with a view template at all times. Any changes to the view are automatically reflected in the model. Likewise, any changes in the model are reflected in the view.

Create either an HTML file or a controller action with an accompanying view named Databinding. Include the following in the view:

@{
    Layout = "";
}
<html>
<body ng-app>
    <h1>Databinding</h1>

    <div ng-init="firstName='John'; lastName='Doe';">
        <strong>First name:</strong> {{firstName}} <br />
        <strong>Last name:</strong> <span ng-bind="lastName" />
    </div>

    <script src="~/lib/angular/angular.js"></script>
</body>
</html>

Notice that you can display model values using either directives or data binding (ng-bind). The resulting page should look like this:

Simple Databinding

Templates

Templates in AngularJS are just plain HTML pages decorated with AngularJS directives and artifacts. A template in AngularJS is a mixture of directives, expressions, filters, and controls that combine with HTML to form the view.

Add another view to demonstrate templates, and add the following to it:

@{
    Layout = "";
}
<html>
<body ng-app>
    <h1>Templates</h1>

    <div ng-init="personName='John Doe'">
        <input ng-model="personName" /> {{personName}}
    </div>

    <script src="~/lib/angular/angular.js"></script>
</body>
</html>

The template has AngularJS directives like ng-app, ng-init, ng-model and data binding expression syntax to bind the personName property. Running in the browser, the view looks like the screenshot below:

Simple Templates example 1

If you change the name by typing in the input field, you will see the text next to the input field dynamically update, showing Angular two-way data binding in action.

Simple Templates example 2

Expressions

Expressions in AngularJS are JavaScript-like code snippets that are written inside the {{ expression }} syntax. The data from these expressions is bound to HTML the same way as ng-bind directives. The main difference between AngularJS expressions and regular JavaScript expressions is that AngularJS expressions are evaluated against the $scope object in AngularJS.

The AngularJS expressions in the sample below bind personName and a simple JavaScript calculated expression:

@{
    Layout = "";
}
<html>
<body ng-app>
    <h1>Expressions</h1>

    <div ng-init="personName='John Doe'">
        Person's name is: {{personName}} <br />
        Simple JavaScript calculation of 1 + 2: {{1+2}}
    </div>

    <script src="~/lib/angular/angular.js"></script>
</body>
</html>

The example running in the browser displays the personName data and the results of the calculation:

Simple Expressions

Repeaters

Repeating in AngularJS is done via a primitive directive called ng-repeat. The ng-repeat directive repeats a given HTML element in a view over the length of a repeating data array. Repeaters in AngularJS can repeat over an array of strings or objects. Here is a sample usage of repeating over an array of strings:

@{
    Layout = "";
}
<html>
<body ng-app>
    <h1>Repeaters</h1>

    <div ng-init="names=['John Doe', 'Mary Jane', 'Bob Parker']">
        <ul>
            <li ng-repeat="name in names">
                {{name}}
            </li>
        </ul>
    </div>

    <script src="~/lib/angular/angular.js"></script>
</body>
</html>

The repeat directive outputs a series of list items in an unordered list, as you can see in the developer tools shown in this screenshot:

Repeater Example

Here is an example that repeats over an array of objects. The ng-init directive establishes a names array, where each element is an object containing first and last names. The ng-repeat assignment, name in names, outputs a list item for every array element.

@{
    Layout = "";
}
<html>
<body ng-app>
    <h1>Repeaters2</h1>

    <div ng-init="names=[
		{firstName:'John', lastName:'Doe'},
		{firstName:'Mary', lastName:'Jane'},
		{firstName:'Bob', lastName:'Parker'}]">
        <ul>
            <li ng-repeat="name in names">
                {{name.firstName + ' ' + name.lastName}}
            </li>
        </ul>
    </div>

    <script src="~/lib/angular/angular.js"></script>
</body>
</html>

The output in this case is the same as in the previous example.

Angular provides some additional directives that can help provide behavior based on where the loop is in its execution.

$index

Use $index in the ng-repeat loop to determine which index position your loop currently is on.

$even and $odd

Use $even in the ng-repeat loop to determine whether the current index in your loop is an even indexed row. Similarly, use $odd to determine if the current index is an odd indexed row.

$first and $last

Use $first in the ng-repeat loop to determine whether the current index in your loop is the first row. Similarly, use $last to determine if the current index is the last row.

Below is a sample that shows $index, $even, $odd, $first, and $last in action:

@{
    Layout = "";
}
<html>
<body ng-app>
    <h1>Repeaters2</h1>

    <div ng-init="names=[
		{firstName:'John', lastName:'Doe'},
		{firstName:'Mary', lastName:'Jane'},
		{firstName:'Bob', lastName:'Parker'}]">
        <ul>
            <li ng-repeat="name in names">
                {{name.firstName + ' ' + name.lastName}} at index {{$index}}
                <span ng-show="{{$first}}">, the first position</span>
                <span ng-show="{{$last}}">, the last position</span>
                <span ng-show="{{$odd}}">,which is odd-numbered.</span>
                <span ng-show="{{$even}}">,which is even-numbered.</span>
            </li>
        </ul>
    </div>

    <script src="~/lib/angular/angular.js"></script>
</body>
</html>

Here is the resulting output:

Repeater example 2

$scope

$scope is a JavaScript object that acts as glue between the view (template) and the controller (explained below). A view template in AngularJS only knows about the values attached to the $scope object in the controller.

Note

In the MVVM world, the $scope object in AngularJS is often defined as the ViewModel. The AngularJS team refers to the $scope object as the Data-Model. Learn more about Scopes in AngularJS.

Below is a simple example showing how to set properties on $scope within a separate JavaScript file, scope.js:

var personApp = angular.module('personApp', []);
personApp.controller('personController', ['$scope', function ($scope) {
    $scope.name = 'Mary Jane';
}]);

Observe the $scope parameter passed to the controller on line 2. This object is what the view knows about. On line 3, we are setting a property called "name" to "Mary Jane".

What happens when a particular property is not found by the view? The view defined below refers to "name" and "age" properties:

@{
    Layout = "";
}
<html>
<body ng-app="personApp">
    <h1>Scope</h1>

    <div ng-controller="personController">
        <strong>Name:</strong> {{name}} <br />
        <strong>Missing Property (age):</strong> {{age}}
    </div>

    <script src="~/lib/angular/angular.js"></script>
    <script src="~/app/scope.js"></script>
</body>
</html>

Notice on line 9 that we are asking Angular to show the "name" property using expression syntax. Line 10 then refers to "age", a property that does not exist. The running example shows the name set to "Mary Jane" and nothing for age. Missing properties are ignored.

Scope example

Modules

A module in AngularJS is a collection of controllers, services, directives, etc. The angular.module() function call is used to create, register, and retrieve modules in AngularJS. All modules, including those shipped by the AngularJS team and third party libraries, should be registered using the angular.module() function.

Below is a snippet of code that shows how to create a new module in AngularJS. The first parameter is the name of the module. The second parameter defines dependencies on other modules. Later in this article, we will be showing how to pass these dependencies to an angular.module() method call.

var personApp = angular.module('personApp', []);

Use the ng-app directive to represent an AngularJS module on the page. To use a module, assign the name of the module, personApp in this example, to the ng-app directive in our template.

<body ng-app="personApp">

Controllers

Controllers in AngularJS are the first point of entry for your code. The <module name>.controller() function call is used to create and register controllers in AngularJS. The ng-controller directive is used to represent an AngularJS controller on the HTML page. The role of the controller in Angular is to set state and behavior of the data model ($scope). Controllers should not be used to manipulate the DOM directly.

Below is a snippet of code that registers a new controller. The personApp variable in the snippet references an Angular module, which is defined on line 2.

// module
var personApp = angular.module('personApp', []);

// controller
personApp.controller('personController', function ($scope) {
    $scope.firstName = "Mary";
    $scope.lastName = "Jane"
});

The view using the ng-controller directive assigns the controller name:

@{
    Layout = "";
}
<html>
<body ng-app="personApp">
    <h1>Controllers</h1>

    <div ng-controller="personController">
        <strong>First Name:</strong> {{firstName}} <br />
        <strong>Last Name:</strong> {{lastName}}
    </div>

    <script src="~/lib/angular/angular.js"></script>
    <script src="~/app/controllers.js"></script>
</body>
</html>

The page shows "Mary" and "Jane" that correspond to the firstName and lastName properties attached to the $scope object:

Controller example

Components

Components in Angular 1.5.x allow for the encapsulation and capability of creating individual HTML elements. In Angular 1.4.x you could achieve the same feature using the .directive() method.

By using the .component() method, development is simplified gaining the functionality of the directive and the controller. Other benefits include; scope isolation, best practices are inherent, and migration to Angular 2 becomes an easier task. The <module name>.component() function call is used to create and register components in AngularJS.

Below is a snippet of code that registers a new component. The personApp variable in the snippet references an Angular module, which is defined on line 2.

// module
var personApp = angular.module('personApp', []);

// controller
var PersonController = function(){
	
	var vm = this;
	vm.firstName = "Aftab";
	vm.lastName = "Ansari";
}

// component
personApp.component('personComponent', {
    templateUrl:'/app/partials/personcomponent.html',
	controller:PersonController,
	controllerAs:'vm'
	
});

The view where we are displaying the custom HTML element.

@{
    Layout = "";
}
<html>
<body ng-app="personApp">
    <h1>Components</h1>

	<person-component></person-component>
   
    <script src="~/lib/angular/angular.js"></script>
    <script src="~/app/components.js"></script>
</body>
</html>

The associated template used by component:

<div>
    <strong>First Name:</strong> {{vm.firstName}} <br />
    <strong>Last Name:</strong> {{vm.lastName}}
</div>

The page shows "Aftab" and "Ansari" that correspond to the firstName and lastName properties attached to the vm object:

Components example

Services

Services in AngularJS are commonly used for shared code that is abstracted away into a file which can be used throughout the lifetime of an Angular application. Services are lazily instantiated, meaning that there will not be an instance of a service unless a component that depends on the service gets used. Factories are an example of a service used in AngularJS applications. Factories are created using the myApp.factory() function call, where myApp is the module.

Below is an example that shows how to use factories in AngularJS:

personApp.factory('personFactory', function () {
    function getName() {
        return "Mary Jane";
    }

    var service = {
        getName: getName
    };

    return service;
});

To call this factory from the controller, pass personFactory as a parameter to the controller function:

personApp.controller('personController', function($scope,personFactory) {
  $scope.name = personFactory.getName();
});

Using services to talk to a REST endpoint

Below is an end-to-end example using services in AngularJS to interact with an ASP.NET Core Web API endpoint. The example gets data from the Web API and displays the data in a view template. Let's start with the view first:

@{
    Layout = "";
}
<html>
<body ng-app="PersonsApp">
    <h1>People</h1>

    <div ng-controller="personController">
        <ul>
            <li ng-repeat="person in people">
                <h2>{{person.FirstName}} {{person.LastName}}</h2>
            </li>
        </ul>
    </div>

    <script src="~/lib/angular/angular.js"></script>
    <script src="~/app/personApp.js"></script>
    <script src="~/app/personFactory.js"></script>
    <script src="~/app/personController.js"></script>
</body>
</html>

In this view, we have an Angular module called PersonsApp and a controller called personController. We are using ng-repeat to iterate over the list of persons. We are referencing three custom JavaScript files on lines 17-19.

The personApp.js file is used to register the PersonsApp module; and, the syntax is similar to previous examples. We are using the angular.module function to create a new instance of the module that we will be working with.

(function () {
    'use strict';
    var app = angular.module('PersonsApp', []);
})();

Let's take a look at personFactory.js, below. We are calling the module’s factory method to create a factory. Line 12 shows the built-in Angular $http service retrieving people information from a web service.

(function () {
    'use strict';

    var serviceId = 'personFactory';

    angular.module('PersonsApp').factory(serviceId,
        ['$http', personFactory]);

    function personFactory($http) {

        function getPeople() {
            return $http.get('/api/people');
        }

        var service = {
            getPeople: getPeople
        };

        return service;
    }
})();

In personController.js, we are calling the module’s controller method to create the controller. The $scope object's people property is assigned the data returned from the personFactory (line 13).

(function () {
    'use strict';

    var controllerId = 'personController';

    angular.module('PersonsApp').controller(controllerId,
        ['$scope', 'personFactory', personController]);

    function personController($scope, personFactory) {
        $scope.people = [];

        personFactory.getPeople().success(function (data) {
            $scope.people = data;
        }).error(function (error) {
            // log errors
        });
    }
})();

Let's take a quick look at the Web API and the model behind it. The Person model is a POCO (Plain Old CLR Object) with Id, FirstName, and LastName properties:

namespace AngularSample.Models
{
    public class Person
    {
        public int Id { get; set; }
        public string FirstName { get; set; }
        public string LastName { get; set; }
    }
}

The Person controller returns a JSON-formatted list of Person objects:

using AngularSample.Models;
using Microsoft.AspNet.Mvc;
using System.Collections.Generic;

namespace AngularSample.Controllers.Api
{
    public class PersonController : Controller
    {
        [Route("/api/people")]
        public JsonResult GetPeople()
        {
            var people = new List<Person>()
            {
                new Person { Id = 1, FirstName = "John", LastName = "Doe" },
                new Person { Id = 1, FirstName = "Mary", LastName = "Jane" },
                new Person { Id = 1, FirstName = "Bob", LastName = "Parker" }
            };

            return Json(people);
        }
    }
}

Let's see the application in action:

Controller displaying REST results

You can view the application's structure on GitHub.

Note

For more on structuring AngularJS applications, see John Papa's Angular Style Guide

 

Note

To create AngularJS module, controller, factory, directive and view files easily, be sure to check out Sayed Hashimi's SideWaffle template pack for Visual Studio. Sayed Hashimi is a Senior Program Manager on the Visual Studio Web Team at Microsoft and SideWaffle templates are considered the gold standard. At the time of this writing, SideWaffle is available for Visual Studio 2012, 2013, and 2015.

Routing and Multiple Views

AngularJS has a built-in route provider to handle SPA (Single Page Application) based navigation. To work with routing in AngularJS, you must add the angular-route library using Bower. You can see in the bower.json file referenced at the start of this article that we are already referencing it in our project.

After you install the package, add the script reference (angular-route.js) to your view.

Now let's take the Person App we have been building and add navigation to it. First, we will make a copy of the app by creating a new PeopleController action called Spa and a corresponding Spa.cshtml view by copying the Index.cshtml view in the People folder. Add a script reference to angular-route (see line 11). Also add a div marked with the ng-view directive (see line 6) as a placeholder to place views in. We are going to be using several additional .js files which are referenced on lines 13-16.

@{
    Layout = "";
}
<html>
<body ng-app="personApp">
    <div ng-view>

    </div>

    <script src="~/lib/angular/angular.js"></script>
    <script src="~/lib/angular-route/angular-route.js"></script>

    <script src="~/app/personModule.js"></script>
    <script src="~/app/personRoutes.js"></script>
    <script src="~/app/personListController.js"></script>
    <script src="~/app/personDetailController.js"></script>
</body>
</html>

Let's take a look at personModule.js file to see how we are instantiating the module with routing. We are passing ngRoute as a library into the module. This module handles routing in our application.

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

The personRoutes.js file, below, defines routes based on the route provider. Lines 4-7 define navigation by effectively saying, when a URL with /persons is requested, use a template called partials/personlist by working through personListController. Lines 8-11 indicate a detail page with a route parameter of personId. If the URL doesn't match one of the patterns, Angular defaults to the /persons view.

personApp.config(['$routeProvider',
    function ($routeProvider) {
        $routeProvider.
            when('/persons', {
                templateUrl: '/app/partials/personlist.html',
                controller: 'personListController'
            }).
            when('/persons/:personId', {
                templateUrl: '/app/partials/persondetail.html',
                controller: 'personDetailController'
            }).
            otherwise({
                redirectTo: '/persons'
            })
    }
]);

The personlist.html file is a partial view containing only the HTML needed to display person list.

<div>
    <h1>PERSONS PAGE</h1>
    <span ng-bind="message"/>
</div>

The controller is defined by using the module's controller function in personListController.js.

personApp.controller('personListController', function ($scope) {
    $scope.message = "You are on the Persons List Page.";
})

If we run this application and navigate to the people/spa#/persons URL, we will see:

Persons List View

If we navigate to a detail page, for example people/spa#/persons/2, we will see the detail partial view:

Person Detail View

You can view the full source and any files not shown in this article on GitHub.

Event Handlers

There are a number of directives in AngularJS that add event-handling capabilities to the input elements in your HTML DOM. Below is a list of the events that are built into AngularJS.

  • ng-click

  • ng-dbl-click

  • ng-mousedown

  • ng-mouseup

  • ng-mouseenter

  • ng-mouseleave

  • ng-mousemove

  • ng-keydown

  • ng-keyup

  • ng-keypress

  • ng-change

Note

You can add your own event handlers using the custom directives feature in AngularJS.

Let's look at how the ng-click event is wired up. Create a new JavaScript file named eventHandlerController.js, and add the following to it:

personApp.controller('eventHandlerController', function ($scope) {
    $scope.firstName = 'Mary';
    $scope.lastName = 'Jane';

    $scope.sayName = function () {
        alert('Welcome, ' + $scope.firstName + ' ' + $scope.lastName);
    }
});

Notice the new sayName function in eventHandlerController on line 5 above. All the method is doing for now is showing a JavaScript alert to the user with a welcome message.

The view below binds a controller function to an AngularJS event. Line 9 has a button on which the ng-click Angular directive has been applied. It calls our sayName function, which is attached to the $scope object passed to this view.

@{
    Layout = "";
}
<html>
<body ng-app="personApp">
    <div ng-controller="eventHandlerController">
        <strong>First Name:</strong> {{firstName}} <br />
        <strong>Last Name:</strong> {{lastName}} <br />
        <input ng-click="sayName()" type="button" value="Say Name" />
    </div>
    <script src="~/lib/angular/angular.js"></script>
    <script src="~/lib/angular-route/angular-route.js"></script>

    <script src="~/app/personModule.js"></script>
    <script src="~/app/eventHandlerController.js"></script>
</body>
</html>

The running example demonstrates that the controller's sayName function is called automatically when the button is clicked.

Click Event

For more detail on AngularJS built-in event handler directives, be sure to head to the documentation website of AngularJS.