Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

Angular JS Application with MVC 6, Web API 2, ASPNET 5 and Gulp – Part 1

0.00/5 (No votes)
6 Jul 2015 1  
Building Angular JS Application using Web API 2 in MVC 6

Introduction

In this article, I explain how to setup your ASP.NET 5 project for a single page application (SPA) in Angular JS. I have used MVC 6 Web API for some static data to display, eventually I will use database to store and display the data using Web API. I will also show how to copy the front-end JavaScript files automatically using gulp, bower and npm. In later modules, I will describe how to combine and minify the front-end JavaScripts files.

In my demo, I have Visual Studio 2015 RC, the latest pre-release version of next generation Visual Studio for cross platform development. I will also use Entity Framework 7 and DNX (.NET Execution Environment) to migrate the database and run the web site in different environments.

Background

Single Page Application (SPA) now-a-days is well-known term due to the advancement of front-end tools and technologies that have been evolved in recent years. Among them, Angular JS is the most popular in building SPAs. Recent release of Microsoft's Visual Studio 2015 RC is a fantastic platform for developing SPAs in which the front-end tools can be easily managed and maintained for the application. Also the Web API 2 in MVC 6 is being used as back end service that are very efficient support for calling it from the front-end.

I will be using Visual Studio 2015 RC, MVC 6 Web API2, gulp, bower and npm throughout the journey to work with front-end and back-end development.

Creating the Project

To start with, I have opened Visual Studio 2015 RC, and from the Start page, I have selected New Project and from the available template, I chose ASP.NET Web Application. I have named the project as BooksAngularApp.

Image 1

After clicking Ok, the template select wizard appears where I will select Web Site from the available ASP.NET 5 Preview Templates. The reason I have chosen the template is that the template provides us a framework that is helpful rather than starting the empty template.

To note here, the Web Site has gulp task runner as the default which I will be using to combine and minify the front-end JavaScripts and CSS files.

Image 2

After clicking OK, I have got the following project created with the project structure and the preview page as below:

Image 3

Task Runner

If we look at the project structure on the Solution Explorer, we can see the pre-installed packages such as bootstrap, jquery, hammer, etc. in the bower folder. Also, we can see the npm folder where gulp and rimraf have been installed as front-end tools that are being used to manage the front-end scripts, especially JavaScripts and CSS files.

Image 4

Now let us have a look at the gulpfile.js which will be used to manage our front-end code or scripts.

var gulp = require("gulp"),
  rimraf = require("rimraf"),
  fs = require("fs");

eval("var project = " + fs.readFileSync("./project.json"));

var paths = {
  bower: "./bower_components/",
  lib: "./" + project.webroot + "/lib/"
};


gulp.task("clean", function (cb) {
  rimraf(paths.lib, cb);

});

gulp.task("copy", ["clean"], function () {
  var bower = {
    "bootstrap": "bootstrap/dist/**/*.{js,map,css,ttf,svg,woff,eot}",
    "bootstrap-touch-carousel": "bootstrap-touch-carousel/dist/**/*.{js,css}",
    "hammer.js": "hammer.js/hammer*.{js,map}",
    "jquery": "jquery/jquery*.{js,map}",
    "jquery-validation": "jquery-validation/jquery.validate.js",
    "jquery-validation-unobtrusive": 
            "jquery-validation-unobtrusive/jquery.validate.unobtrusive.js"
  }

  for (var destinationDir in bower) {
    gulp.src(paths.bower + bower[destinationDir])
      .pipe(gulp.dest(paths.lib + destinationDir));

  }
});

We can see the scripts runs some tasks to copy and clean the folders for the JavaScripts and CSS files in the wwwroot folders whenever any change is made to the files during development.

Let us see how these happen. I have opened the Task Runner from View->Other Windows->Task Runner.

Image 5

We can the clean and copy tasks as defined in the gulpfile.js. Before we running any of them, we can see a lib folder has a copy of all the JavaScripts and CSS files by default.

I have run the clean task and it cleans the lib folder in wwwroot directory, that means gulp is being used to run the clean task to remove the static files.

Image 6

I have run the copy task and the lib folder is back with the copied files again.

Image 7

Installing Angular Packages

For our SPA, I will be using three angular packages: angular, angular-route and angular-resource. Currently, the default bower.json file looks like below:

>
{
  "name": "ASP.NET",
  "private": true,
  "dependencies": {
    "bootstrap": "3.0.0",
    "jquery": "1.10.2",
    "jquery-validation": "1.11.1",
    "jquery-validation-unobtrusive": "3.2.2",
    "hammer.js": "2.0.4",
    "bootstrap-touch-carousel": "0.8.0"
  }
}

I have added the three angular packages as below:

{
  "name": "ASP.NET",
  "private": true,
  "dependencies": {
    "bootstrap": "3.0.0",
    "jquery": "1.10.2",
    "jquery-validation": "1.11.1",
    "jquery-validation-unobtrusive": "3.2.2",
    "hammer.js": "2.0.4",
    "bootstrap-touch-carousel": "0.8.0",
    "angular": "*",
    "angular-route": "*",
    "angular-resource": "*"
  }
}

After adding the three lines for angular, I haven't saved the bower.json file yet, and in the Solution Explorer in Bower folder, there are no angular modules.

Image 8

After saving the file, we can see the angular components on the bower folder in the dependencies list after few seconds.

Image 9

You might see the bower folder as below where the angular modules are not installed:

Image 10

If they are not installed, right click on the Bower folder and click on Restore Packages and it will restore all the uninstalled packages that are added to the bower.json file.

Image 11

Creating gulp Tasks for Angular Scripts

So I have got angular packages installed now and we need to update the task runner so that the angular files are copied over the lib folder with the other bower components.

I have updated the copy task as below:

gulp.task("copy", ["clean"], function () {
  var bower = {
    "bootstrap": "bootstrap/dist/**/*.{js,map,css,ttf,svg,woff,eot}",
    "bootstrap-touch-carousel": "bootstrap-touch-carousel/dist/**/*.{js,css}",
    "hammer.js": "hammer.js/hammer*.{js,map}",
    "jquery": "jquery/jquery*.{js,map}",
    "jquery-validation": "jquery-validation/jquery.validate.js",
    "jquery-validation-unobtrusive": 
            "jquery-validation-unobtrusive/jquery.validate.unobtrusive.js",
    "angular": "angular/angular*.{js,map}",
    "angular-route": "angular-route/angular-route*.{js,map}",
    "angular-resource": "angular-resource/angular-resource*.{js,map}"
  }

Now I have run the copy task again and I can see the angular files are copied over to the lib folder of wwwroot.

Image 12

I have configured the copy and copyapp tasks to be run after every build of the application to make sure the changes are copied over.

Image 13

Now we are ready to start working with the SPA BooksAngularApp.

Creating Data Model and Web API

For the SPA, I will be using a fictitious book store where we will be able to view the list of books and their details. In this module, I will be using some static data for the book items. For this purpose, I need a data model that will be used for accessing and displaying the books.

I have added a new class called Book in the Models folder of my project and the model looks like below:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using System.ComponentModel.DataAnnotations;

namespace BooksAngularApp.Models
{
   /// <summary>
   /// The entity class with Book properties
   /// </summary>
    public class Book
    {
        [Key]
        public int Id { get; set; }
        public string Title { get; set; }
        public string Author { get; set; }
        public string Type { get; set; }
        public DateTime DatePublished { get; set; }
        public decimal Price { get; set; }
    }
}

After this, I have created an MVC Controller Class named as BooksController which will be the API Controller for our data access back and forth.

Image 14

As default, the Controller class looks like below:

Image 15

I have made changes to the class to be as an API Controller by adding the...

[Route("api/[controller]")]

...before the class definition so that by the Route attribute, it will act as an API Controller.

I have added the HttpGet method to display the static list of books as below:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNet.Mvc;
using BooksAngularApp.Models;

namespace BooksAngularApp.Controllers
{
    [Route("api/[controller]")]
    public class BooksController : Controller
    {
        [HttpGet]
        public IEnumerable<Book> Get()
        {
            return new List<Book>
                {
                    new Book {Id=1, Title="Wonders of the Sky", 
                    Author="Martin James", DatePublished=Convert.ToDateTime("1/1/2013"), 
                    Type="Science",Price=23.23m },
                    new Book {Id=2, Title="Secrets of the Mind ", 
                    Author="Allan Sue ", DatePublished=Convert.ToDateTime("2/1/2011"), 
                    Type="Psychology", Price=12.50m },
                    new Book {Id=3, Title="We are Alive", 
                    Author="Dick Smith", DatePublished=Convert.ToDateTime("2/11/2010"), 
                    Type="Science Fiction", Price=21.25m } ,
                    new Book {Id=4, Title="Last day of the world", 
                    Author="Martin James", DatePublished=Convert.ToDateTime("1/1/2013"), 
                    Type="History", Price=10.40m },

                };
        }
    }
}

Image 16

To see whether the API Controller that I created is working, let us run the API from the browser:

Image 17

We can see the data is being displayed as JSON from the API Controller, that means, we are ready to use the API to display the data.

Writing Angular Code

I have created an app folder in my application root where I will be placing all angular related modules such as controllers, services, etc.

I have added app.js as the first module as below:

(function () {

    'use strict';

    angular.module('booksApp', ['booksServices']);

})();

The booksApp Angular module will depend on the service called booksServices which I will define now.

I have created a new folder called services within app folder and I have added booksServices.js as below:

(function () {
    'use strict';
    var booksServices = angular.module('booksServices', ['ngResource']);

    booksServices.factory('Books', ['$resource',
      function ($resource) {
          return $resource('/api/books/', {}, {
              query: { method: 'GET', params: {}, isArray: true }
          });
      }]);

})();

The service will inject ngResource as dependency which will eventually call the API Controller to get the data from the API.

Now we need another Angular module which we will call booksController which I have created in controllers folder within app folder named as booksController.js.

(function () {
    'use strict';
    angular
    .module('booksApp')
    .controller('booksController',booksController)

    booksController.$inject = ['$scope', 'Books'];
    function booksController($scope, Books)
    {
        $scope.books = Books.query();
    }
})();

Here we can see, the controller injects the scope object which returns the query results from the API.

Writing Tasks for App Scripts

We need to write the task to copy the files over to the wwwroot folder as with other JavaSript files we did before.

var paths = {
  bower: "./bower_components/",
  lib: "./" + project.webroot + "/lib/",
  app: "./" + project.webroot + "/app/",
  srcapp:  "./app/",
};

gulp.task("cleanappp", function (cb) {
    rimraf(paths.app, cb);

});

gulp.task("copyapp",["cleanappp"], function () {
    var app = {
        "controllers": "controllers/booksController.js",
        "services": "services/booksServices*.js",
        "/": "app.js"
    }

    for (var destinationDir in app) {
        gulp.src(paths.srcapp + app[destinationDir])
          .pipe(gulp.dest(paths.app + destinationDir));
    }
});

I have added the app and srcapp in the paths variable to define the source and destination for the app scripts. I have added a new task called cleanapp which will be used to clean the app folder in wwwroot. Finally, I have added another task called copyapp which has a inject of cleanapp task that means before copying the app scripts, it will clean up the folder first and then copy all script files.

<!DOCTYPE html>
<html ng-app="booksApp">
<head>
    <meta charset="utf-8" />
    <title>List of Books</title>
    <link href="lib/bootstrap/css/bootstrap.css" rel="stylesheet" />
    <link href="lib/bootstrap/css/bootstrap-theme.css" rel="stylesheet" />
    <script src="lib/jquery/jquery.min.js"></script>
    <script src="lib/angular/angular.js"></script>
    <script src="lib/bootstrap/js/bootstrap.js"></script>
    <script src="lib/angular-resource/angular-resource.js"></script>
    <script src="app/app.js"></script>
    <script src="app/controllers/booksController.js"></script>
    <script src="app/services/booksServices.js"></script>
</head>
<body ng-cloak>
    <div ng-controller="booksController">
        <table class="table table-bordered table-striped">
            <thead>
                <tr>
                    <th>Title</th>
                    <th>Author</th>
                    <th>Type</th>
                    <th>Date Published</th>
                    <th>Price</th>
                </tr>
            </thead>
            <tbody>
                <tr ng-repeat="book in books">
                    <td>{{book.Title}}</td>
                    <td>{{book.Author}}</td>
                    <td>{{book.Type}}</td>
                    <td>{{book.DatePublished|date:"dd/MM/yyyy"}}</td>
                    <td>${{book.Price}}</td>
                </tr>
            </tbody>
        </table>
    </div>
</body>
</html>

Image 18

Image 19

Here, the web site is running both IIS Express and WebListener - side-by-side.

Image 20

History

  • 16th May, 2015: Initial submission
  • 24th May, 2015: Added ZIP solution to the article

Source Code

The source code for this article is available in the GitHub at: https://github.com/mostafaasad/ASPNET5/tree/master/BooksAngular-Part1/BooksAngularApp

Next

In the next module, I will explore searching and filtering data and migration of the database using Entity Framework 7 and DNX.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here