Introduction
Quote:
Update: ASP.NET 5 has been renamed to ASP.NET Core. Check my recent article on getting started with Angular2 in ASP.NET Core.
This post will walk you through the step-by-step procedure on building a simple ASP.NET 5 application using AngularJS with Web API.
Before we dig further, let’s talk about a quick overview of AngularJS and Web API in MVC 6.
Introducing AngularJS
AngularJS is a client-side MVC framework written in JavaScript. It runs in a web browser and greatly helps us (developers) to write modern, single-page, AJAX-style web applications. It is a general purpose framework, but it shines when used to write CRUD (Create Read Update Delete) type web applications.
Introducing Web API
ASP.NET Web API is a framework that makes it easy to build HTTP services that reach a broad range of clients, including browsers and mobile devices. ASP.NET Web API is an ideal platform for building RESTful applications on the .NET Framework. In ASP.NET 5, Web API is now part of MVC 6. Read more here. If you are new to ASP.NET 5 then I would suggest you to read the following articles below to know more about the new features in ASP.NET:
Creating an ASP.NET 5 Project
To start, fire up Visual Studio 2015 and create a new ASP.NET 5 project by selecting File > New Project. In the dialog, under Templates > Visual C#, select ASP.NET Web Application as shown in the figure below:
Name your project to whatever you like and then click OK. For this example, I named the project as “AngularJS101
”. Now after that, you should be able to see the “New ASP.NET Project” dialog:
Now select ASP.NET 5 Preview Empty template from the dialog above. Then click OK to let Visual Studio generate the necessary files and templates needed for you. You should be able to see something like below:
Adding the Scripts Folder
The next thing to do is to create a new folder called “Scripts”. This folder will contain all the JavaScript files needed in our application:
Getting the Required Packages
ASP.NET 5 now supports three main package managers: NuGet, NPM and Bower.
Package Manager
A package manager enables you to easily gather all resources that you need for building an application. In other words, you can make use of package manager to automatically download all the resources and their dependencies instead of manually downloading project dependencies such as jQuery, Bootstrap and AngularJS in the web.
NuGet
NuGet manages .NET packages such as Entity Framework, ASP.NET MVC and so on. You typically specify the NuGet packages that your application requires within project.json file.
NPM
NPM is one of the newly supported package managers in ASP.NET 5. This package manager was originally created for managing packages for the open-source NodeJS framework. The package.json is the file that manages your project’s NPM packages.
Bower
Bower is another supported package manager in ASP.NET 5. It was created by Twitter that is designed to support front-end development. You can use Bower to manage client-side resources such as jQuery, AngularJS and Bootstrap.
For this example, we need to use NPM to install the resources we need in our application such as Grunt and the Grunt plugins. To do this, just right click in your Project (in this case AngularJS101
) and select Add > New Item. In the dialog, select NPM configuration file as shown in the figure below:
Click Add to generate the file for you. Now open package.json file and modify it by adding the following dependencies:
{
"version": "1.0.0",
"name": "AngularJS101",
"private": true,
"devDependencies": {
"grunt": "0.4.5",
"grunt-contrib-uglify": "0.9.1",
"grunt-contrib-watch": "0.6.1"
}
}
Notice that you get Intellisense support while you edit the file. A matching list of NPM package names and versions shows as you type.
In package.json file, from the code above, we have added three (3) dependencies named grunt
, grunt-contrib-uglify
and grunt-contrib-watch
NPM packages that are required in our application.
Now save the package.json file and you should be able to see a new folder under Dependencies named NPM as shown in the following:
Right click on the NPM folder and select Restore Packages to download all the packages required. Note that this may take a bit to finish the download, so just be patient and wait ;). After that, the grunt
, grunt-contrib-uglify
and grunt-contrib-watch
NPM packages should now be installed as shown in the following:
Configuring Grunt
Grunt is an open-source tool that enables you to build client-side resources for your project. For example, you can use Grunt to compile your LESS or Saas files into CSS. Adding to that, Grunt can also be used to minify CSS and JavaScript files.
In this example, we will use Grunt to combine and minify JavaScript files. We will configure Grunt so that it will take all the JavaScript files from the Scripts folder that we created earlier, combine and minify the files, and finally save the results to a file named app.js within the wwwroot folder.
Now right click on your project and select Add > New Item. Select Grunt Configuration file from the dialog as shown in the figure below:
Then, click Add to generate the file and then modify the code within the Gruntfile.js file so it will look like this:
module.exports = function (grunt) {
grunt.loadNpmTasks('grunt-contrib-uglify');
grunt.loadNpmTasks('grunt-contrib-watch');
grunt.initConfig({
uglify: {
my_target: {
files: { 'wwwroot/app.js': ['Scripts/app.js', 'Scripts/**/*.js'] }
}
},
watch: {
scripts: {
files: ['Scripts/**/*.js'],
tasks: ['uglify']
}
}
});
grunt.registerTask('default', ['uglify', 'watch']);
};
The code above contains three sections. The first one is used to load each of the Grunt plugins that we need from the NPM packages that we configured earlier. The initConfig()
is responsible for configuring the plugins. The Uglify
plugin is configured so that it combines and minifies all the files from the Scripts folder and generate the result in a file named app.js within wwwroot folder. The last section contains the definitions for your tasks. In this case, we define a single ‘default’ task that runs ‘uglify’ and then watches for changes in our JavaScript file.
Now save the file and let’s run the Grunt file using Visual Studio Task Runner Explorer. To do this, go to View > Other Windows > Task Runner Explorer in Visual Studio main menu. In the Task Runner Explorer, make sure to hit the refresh button to load the tasks for our application. You should see something like this:
Now, right click on the default task and select Run. You should be able to see the following output:
Configuring ASP.NET MVC
There are two main files that we need to modify to enable MVC in our ASP.NET 5 application.
First, we need to modify the project.json file to include MVC 6 under dependencies:
"webroot": "wwwroot",
"version": "1.0.0-*",
"dependencies": {
"Microsoft.AspNet.Server.IIS": "1.0.0-beta3",
"Microsoft.AspNet.Mvc": "6.0.0-beta3"
},
"frameworks": {
"aspnet50": { },
"aspnetcore50": { }
},
Make sure to save the file to restore the packages required. The project.json file is used by the NuGet package manager to determine the packages required in your application. In this case, we’ve added Microsoft.AspNet.Mvc
.
Now the last thing is to modify the Startup.cs file to add the MVC framework in the application pipeline. Your Startup.cs file should now look like this:
using System;
using Microsoft.AspNet.Builder;
using Microsoft.AspNet.Http;
using Microsoft.Framework.DependencyInjection;
namespace AngularJS101
{
public class Startup
{
public void ConfigureServices(IServiceCollection services){
services.AddMvc();
}
public void Configure(IApplicationBuilder app){
app.UseMvc();
}
}
}
The ConfigureServices()
method is used to register MVC with the ASP.NET 5 built-in Dependency Injection Framework (DI). The Configure()
method is used to register MVC with OWIN.
Adding Models
The next step is to create a model that we can use to pass data from the server to the browser/client. Now create a folder named “Models” under the root of your project. Within the “Models” folder, create a class named “DOTAHero
” and add the following code below:
using System;
namespace AngularJS101.Models
{
public class DOTAHero
{
public int ID { get; set; }
public string Name { get; set; }
public string Type { get; set; }
}
}
Create another class called “HeroManager
” and add the following code below:
using System.Collections.Generic;
using System.Linq;
namespace AngularJS101.Models
{
public class HeroManager
{
readonly List<DOTAHero> _heroes = new List<DOTAHero>() {
new DOTAHero { ID = 1, Name = "Bristleback", Type="Strength"},
new DOTAHero { ID = 2, Name ="Abbadon", Type="Strength"},
new DOTAHero { ID = 3, Name ="Spectre", Type="Agility"},
new DOTAHero { ID = 4, Name ="Juggernaut", Type="Agility"},
new DOTAHero { ID = 5, Name ="Lion", Type="Intelligence"},
new DOTAHero { ID = 6, Name ="Zues", Type="Intelligence"},
new DOTAHero { ID = 7, Name ="Trent", Type="Strength"},
};
public IEnumerable<DOTAHero> GetAll { get { return _heroes; } }
public List<DOTAHero> GetHeroesByType(string type) {
return _heroes.Where(o => o.Type.ToLower().Equals(type.ToLower())).ToList();
}
public DOTAHero GetHeroByID(int Id) {
return _heroes.Find(o => o.ID == Id);
}
}
}
The HeroManager
class contains a readonly
property that contains a list of heroes. For simplicity, the data is obviously static
. In real scenario, you may need to get the data in a storage medium such as database or any files that stores your data. It also contains a GetAll
property that returns all the heroes and a GetHeroesByType()
method that returns a list of heroes based on the hero type, and finally a GetHeroByID()
method that returns a hero based on their ID.
Adding Web API Controller
For this particular example, we will be using Web API for passing data to the browser/client.
Unlike previous versions of ASP.NET, MVC and Web API controllers used the same controller base class. Since Web API is now part of MVC 6, then we can start creating Web API controllers because we already pulled the required NuGet packages for MVC 6 and configured MVC 6 in startup.cs.
Now add an “API” folder under the root of the project:
Then add a Web API controller by right-clicking the API folder and selecting Add > New Item. Select Web API Controller Class and name the controller as “HeroesController
” as shown in the figure below:
Click Add to generate the file for you. Now modify your HeroesController
class so it will look like this:
using System.Collections.Generic;
using Microsoft.AspNet.Mvc;
using AngularJS101.Models;
namespace AngularJS101.API.Controllers
{
[Route("api/[controller]")]
public class HeroesController : Controller
{
[HttpGet]
public IEnumerable<DOTAHero> Get()
{
HeroManager HM = new HeroManager();
return HM.GetAll;
}
[HttpGet("{id}")]
public DOTAHero Get(int id)
{
HeroManager HM = new HeroManager();
return HM.GetHeroByID(id);
}
}
}
At this point, we will only be focusing on GET
methods to retrieve data. The first GET
method returns all the heroes available by calling the GetAll
property found in HeroManager
class. The second GET
method returns a specific hero data based on the ID
.
You can test whether the actions are working by running your application in the browser and appending the /api/heroes in the URL. Here are the outputs for both GET
actions:
Route: /api/heroes
Route: /api/heroes/7
Creating an AngularJS Application
Visual Studio 2015 includes templates for creating AngularJS modules, controllers, directives and factories. For this example, we will be displaying the list of heroes using an AngularJS template.
Adding an AngularJS Module
To get started, let's create an AngularJS module by right-clicking on the Scripts folder and selecting Add > New Item. Select AngularJS Module as shown in the figure below:
Click Add to generate the file and copy the following code for our AngularJS module:
(function () {
'use strict';
angular.module('heroesApp', [
'heroesService'
]);
})();
The code above defines a new AngularJS module named “heroesApp
”. The heroesApp
has a dependency on another AngularJS module named “heroesService
” which we will create later in the next step.
Adding an AngularJS Controller
The next thing to do is to create a client-side AngularJS Controller. Create a new folder called “Controllers” under the Script folder as in the following:
Then, right click on the Controllers folder and select Add > New Item. Select AngularJS Controller using $scope as shown in the figure below:
Click Add and copy the following code below within your heroesController.js file:
(function () {
'use strict';
angular
.module('heroesApp')
.controller('heroesController', heroesController);
heroesController.$inject = ['$scope','Heroes'];
function heroesController($scope, Heroes) {
$scope.Heroes = Heroes.query();
}
})();
`
The code above depends on the Heroes service that supplies the list of heroes. The Heroes service is passed to the controller using dependency injection (DI). The $inject()
method call enables DI to work. The Heroes service is passed as the second parameter to the heroesController()
function.
Adding the Heroes Service
We will use an AngularJS Heroes service to interact with our data via Web API. Now add a new folder called “Services” within the Script folder. Right click on the Services folder and select Add > New Item. From the dialog, select AngularJS Factory and name it as “heroesService.js” as in the following:
Now click Add and then replace the generated default code with the following:
(function () {
'use strict';
var heroesService = angular.module('heroesService', ['ngResource']);
heroesService.factory('Heroes', ['$resource',
function ($resource) {
return $resource('/api/heroes', {}, {
query: { method: 'GET', params: {}, isArray: true}
});
}
]);
})();
The code above basically returns a list of heroes from the Web API action. The $resource
object performs an AJAX request using a RESTful pattern. The heroesService
is associated with the /api/heroes route on the server. This means that when you perform a query against the service from your client-side code, the Web API HeroesController
is invoked to return a list of heroes.
Adding an AngularJS Template
Let’s add an AngularJS template for displaying the list of heroes. To do this, we will need an HTML page to render in the browser. In the wwwroot folder, add a new HTML page and name it as “index” for simplicity. Your application structure should now look like this:
The wwwroot folder is a special folder in your application. The purpose is that the wwwroot folder should contain all contents of your website such as HTML files and images needed for your website.
You should not place any of your source code within the wwwroot folder. Instead, source codes such as MVC controllers’ source, model classes and unminified JavaScript and LESS files should be placed outside of the wwwroot folder.
Now, replace the content of index.html with the following:
<!DOCTYPE html>
<html ng-app="heroesApp">
<head>
<meta charset="utf-8" />
<title>DOTA 2 Heroes</title>
<script src="//ajax.googleapis.com/ajax/libs/angularjs/1.3.15/angular.js"></script>
<script src="//ajax.googleapis.com/ajax/libs/angularjs/1.3.15/angular-resource.js"></script>
<script src="app.js"></script>
</head>
<body ng-cloak>
<div ng-controller="heroesController">
<h1>DOTA Heroes</h1>
<table>
<thead>
<tr>
<th>ID</th>
<th>Name</th>
<th>Type</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="hero in Heroes">
<td>{{hero.ID}}</td>
<td>{{hero.Name}}</td>
<td>{{hero.Type}}</td>
</tr>
</tbody>
</table>
</div>
</body>
</html>
There are several things to point out from the markup above:
The html
element is embedded with the ng-app
directive. This directive associates the heroesApp
with the HTML file.
In the script
section, you will notice that I use Google CDN for referencing AngularJS and related libraries. Besides being lazy, it’s my intent to use CDN for referencing standard libraries such as jQuery, AngularJS and Bootstrap to boost application performance. If you don’t want to use CDN, then you can always install AngularJS packages using Bower.
The body
element is embedded with the ng-cloak
directive. This directive hides an AngularJS template until the data has been loaded in the page.
The div
element within the body
block is embedded with the ng-controller
directive. This directive associates the heroesController
and renders the data within the div
element.
Finally, the ng-repeat
directive is added to the tr
element of the table. This will create row for each data that retrieved from the server.
Output
Here’s the output below when running the page and navigating to index.html:
That’s it! I hope you will find this post useful and fun. Stay tuned for more!
CodeProject