Click here to Skip to main content
12,758,462 members (32,116 online)
Click here to Skip to main content
Add your own
alternative version

Stats

67K views
5.8K downloads
61 bookmarked
Posted 26 Jan 2016

CRUD in ASP.NET MVC 5 using WebAPI 2 with AngularJS

, 22 Feb 2016 CPOL
Rate this:
Please Sign up or sign in to vote.
This tip will help beginners to implement CRUD operations in ASP.NET MVC 5 using WebAPI 2 with scripting language as AngularJS and Database as MS SQL 2008R2.

Introduction

As you know, AngularJS is the most popular JavaScript framework to develop single page applications. You can also use Angular for Insert, Update, Delete and Retrieve operations. This tip will demonstrate how to use Angular with MVC5 and WebAPI2 for CRUD (Create, Read, Update, Delete) operations. Many experienced developers will find this tip very basic, but since the tip is written from the perspective of beginners, I've tried to keep things simple.

Background

In my previous article, I demonstrated CRUD operations with KnockoutJS. Here, I will be using the same database, design and contents, but instead of Knockout, I will be using AngularJS and operations will be handled in WebAPI.

Using the Code

Let's begin !!!

Create 'TblProductList' table with this schema.

Create a new project in ASP.NET MVC 5 and name it as you prefer and select empty project template.
Tick MVC and Web API under Add folders and core references for:

Install Entity Framework 6, Jquery and AngularJS in your project using NuGet Package Manager.
You can also download jquery.js and angular.js from their official website and paste it in 'Scripts' folder of your project.

Right click on Model folder and add a new ADO.NET Entity Data Model. Name it as 'ProductDataContext.edmx'.

Choose 'Generate from Database' and configure the connection string as per your SQL server.

After generating the model, you will get the entity of TblProductList.

Create new folder 'Interface' in root directory. Add a new class 'IProductRepository.cs'.

interface IProductRepository
  {
      IEnumerable<TblProductList> GetAll();
      TblProductList Get(int id);
      TblProductList Add(TblProductList item);
      bool Update(TblProductList item);
      bool Delete(int id);
  }

Create new folder 'Repositories' in root directory. Add a new class 'ProductRepository.cs'. Implement the methods to Create, Read, Update, Delete using Entity Framework.

public class ProductRepository : IProductRepository
   {
       ProductDBEntities ProductDB = new ProductDBEntities();

       public IEnumerable<TblProductList> GetAll()
       {
           // TO DO : Code to get the list of all the records in database
           return ProductDB.TblProductLists;
       }

       public TblProductList Get(int id)
       {
           // TO DO : Code to find a record in database
           return ProductDB.TblProductLists.Find(id);
       }

       public TblProductList Add(TblProductList item)
       {
           if (item == null)
           {
               throw new ArgumentNullException("item");
           }

           // TO DO : Code to save record into database
           ProductDB.TblProductLists.Add(item);
           ProductDB.SaveChanges();
           return item;
       }

       public bool Update(TblProductList item)
       {
           if (item == null)
           {
               throw new ArgumentNullException("item");
           }

           // TO DO : Code to update record into database
           var products = ProductDB.TblProductLists.Single(a => a.Id == item.Id);
           products.Name = item.Name;
           products.Category = item.Category;
           products.Price = item.Price;
           ProductDB.SaveChanges();

           return true;
       }

       public bool Delete(int id)
       {
           // TO DO : Code to remove the records from database
           TblProductList products = ProductDB.TblProductLists.Find(id);
           ProductDB.TblProductLists.Remove(products);
           ProductDB.SaveChanges();
           return true;
       }
   }

Right click on Controllers folder and add new WebAPI 2 Empty Controller 'ProductController.cs':

public class ProductController : ApiController
   {
       static readonly IProductRepository repository = new ProductRepository();

       public IEnumerable GetAllProducts()
       {
           return repository.GetAll();
       }

       public TblProductList PostProduct(TblProductList item)
       {
           return repository.Add(item);
       }

       public IEnumerable PutProduct(int id, TblProductList product)
       {
           product.Id = id;
           if (repository.Update(product))
           {
               return repository.GetAll();
           }
           else
           {
               return null;
           }
       }

       public bool DeleteProduct(int id)
       {
           if (repository.Delete(id))
           {
               return true;
           }
           else
           {
               return false;
           }
       }
   }

Right click on Controllers folder and add new Controller 'HomeController.cs':

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

Right click on ActionResult Product() and add a view 'Product.cshtml'.

@{
    ViewBag.Title = "Products List";
    Layout = "~/Views/Shared/_Layout.cshtml";
}
@section scripts {

    <link href="~/Content/CustomStyle.css" rel="stylesheet" />
    <script src="~/Scripts/jquery-1.10.2.min.js"></script>
    <script src="~/Scripts/angular.js"></script>
    <script src="~/Scripts/AngularDemo.js"></script>
}
<div ng-app="demoModule" id="body">
    <div ng-controller="demoCtrl">
        <h2>AngularJS CRUD Operations with MVC5 WebAPI</h2>

        <h3>List of Products</h3>

        <table ng-cloak>
            <thead>
                <tr>
                    <th style="display: none;">ID</th>
                    <th>Name</th>
                    <th>Category</th>
                    <th>Price</th>
                    <th>Actions</th>
                </tr>
            </thead>
            <tbody>
                <tr ng-repeat="items in productsData">
                    <td hidden="hidden">{{items.Id}}</td>
                    <td>{{items.Name}}</td>
                    <td>{{items.Category}}</td>
                    <td>{{items.Price | currency:'&#8377;':2}}</td>
                    <td>
                   <button ng-model="$scope.Product" 
                   ng-click="edit(productsData[$index])">Edit</button>
                   <button ng-click="delete($index)">Delete</button>
                    </td>
                </tr>
            </tbody>
            <tfoot>
                <tr>
                    <td colspan="6">
                        <hr />
                    </td>
                </tr>
                <tr>
                    <td>Total :</td>
                    <td></td>
                    <td><label ng-bind="total() | 
                    currency:'&#8377;':2"></label></td>
                    <td></td>
                </tr>
            </tfoot>
        </table>
        <br />
        <div style="border-top: solid 2px #282828; width: 430px; height: 10px"> </div>

        <div ng-show="Product.Id != '' ">
            <div>
                <h2>Update Product</h2>
            </div>
            <div hidden="hidden">
                <label for="id">Id</label>
                <input type="text" data-ng-model="Product.Id" />
            </div>
            <div>
                <label for="name">Name</label>
                <input type="text" data-ng-model="Product.Name" />
            </div>

            <div>
                <label for="category">Category</label>
                <input type="text" data-ng-model="Product.Category" />
            </div>

            <div>
                <label for="price">Price</label>
                <input type="text" data-ng-model="Product.Price" />
            </div>
            <br />
            <div>
                <button data-ng-click="update()">Update</button>
                <button data-ng-click="cancel()">Cancel</button>
            </div>
        </div>

        <div ng-hide="Product.Id != '' ">
            <div>
                <h2>Add New Product</h2>
            </div>
            <div>
                <label for="name">Name</label>
                <input type="text" data-ng-model="Product.Name" />
            </div>

            <div>
                <label for="category">Category</label>
                <input type="text" data-ng-model="Product.Category" />
            </div>

            <div>
                <label for="price">Price</label>
                <input type="text" data-ng-model="Product.Price" />
            </div>
            <br />
            <div>
                <button data-ng-click="save()">Save</button>
                <button data-ng-click="clear()">Clear</button>
            </div>
        </div>
    </div>
</div>

Create a new JavaScript file 'AngularDemo.js' in Scripts folder to implement CRUD operations using Angular code.

// Defining angularjs module
var app = angular.module('demoModule', []);

// Defining angularjs Controller and injecting ProductsService
app.controller('demoCtrl', function ($scope, $http, ProductsService) {

    $scope.productsData = null;
    // Fetching records from the factory created at the bottom of the script file
    ProductsService.GetAllRecords().then(function (d) {
        $scope.productsData = d.data; // Success
    }, function () {
        alert('Error Occured !!!'); // Failed
    });

    // Calculate Total of Price After Initialization
    $scope.total = function () {
        var total = 0;
        angular.forEach($scope.productsData, function (item) {
            total += item.Price;
        })
        return total;
    }

    $scope.Product = {
        Id: '',
        Name: '',
        Price: '',
        Category: ''
    };

    // Reset product details
    $scope.clear = function () {
        $scope.Product.Id = '';
        $scope.Product.Name = '';
        $scope.Product.Price = '';
        $scope.Product.Category = '';
    }

    //Add New Item
    $scope.save = function () {
        if ($scope.Product.Name != "" &&
       $scope.Product.Price != "" && $scope.Product.Category != "") {
            // Call Http request using $.ajax

            //$.ajax({
            //    type: 'POST',
            //    contentType: 'application/json; charset=utf-8',
            //    data: JSON.stringify($scope.Product),
            //    url: 'api/Product/PostProduct',
            //    success: function (data, status) {
            //        $scope.$apply(function () {
            //            $scope.productsData.push(data);
            //            alert("Product Added Successfully !!!");
            //            $scope.clear();
            //        });
            //    },
            //    error: function (status) { }
            //});

            // or you can call Http request using $http
            $http({
                method: 'POST',
                url: 'api/Product/PostProduct/',
                data: $scope.Product
            }).then(function successCallback(response) {
                // this callback will be called asynchronously
                // when the response is available
                $scope.productsData.push(response.data);
                $scope.clear();
                alert("Product Added Successfully !!!");
            }, function errorCallback(response) {
                // called asynchronously if an error occurs
                // or server returns response with an error status.
                alert("Error : " + response.data.ExceptionMessage);
            });
        }
        else {
            alert('Please Enter All the Values !!');
        }
    };

    // Edit product details
    $scope.edit = function (data) {
        $scope.Product = { Id: data.Id, Name: data.Name, Price: data.Price, Category: data.Category };
    }

    // Cancel product details
    $scope.cancel = function () {
        $scope.clear();
    }

    // Update product details
    $scope.update = function () {
        if ($scope.Product.Name != "" &&
       $scope.Product.Price != "" && $scope.Product.Category != "") {
            $http({
                method: 'PUT',
                url: 'api/Product/PutProduct/' + $scope.Product.Id,
                data: $scope.Product
            }).then(function successCallback(response) {
                $scope.productsData = response.data;
                $scope.clear();
                alert("Product Updated Successfully !!!");
            }, function errorCallback(response) {
                alert("Error : " + response.data.ExceptionMessage);
            });
        }
        else {
            alert('Please Enter All the Values !!');
        }
    };

    // Delete product details
    $scope.delete = function (index) {
        $http({
            method: 'DELETE',
            url: 'api/Product/DeleteProduct/' + $scope.productsData[index].Id,
        }).then(function successCallback(response) {
            $scope.productsData.splice(index, 1);
            alert("Product Deleted Successfully !!!");
        }, function errorCallback(response) {
            alert("Error : " + response.data.ExceptionMessage);
        });
    };

});

// Here I have created a factory which is a popular way to create and configure services.
// You may also create the factories in another script file which is best practice.

app.factory('ProductsService', function ($http) {
    var fac = {};
    fac.GetAllRecords = function () {
        return $http.get('api/Product/GetAllProducts');
    }
    return fac;
});

Note

You can use $.ajax from jQuery script or $http from angular.js script to make HTTP request.
It's better to use $http because using $.ajax is also forcing us to use $scope.apply which is not needed if you use $http.

You can also use $resource which is considered to be best practice for doing CRUD operations in RESTful Web API. This tutorial is for beginners, so I tried to keep things simple by using $http.

As per the architecture perspective, instead of controller, you can call $http request for POST, PUT, DELETE in our custom factory like I have done for $http.get(), so that our controller will look clean and exhibits proper Separation of Concern.

Now, add a Layout view '_Layout.cshtml'.

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="utf-8" />
    <title>@ViewBag.Title</title>
    <meta name="viewport" content="width=device-width" />
</head>
<body>
    <div id="body">
        @RenderSection("featured", required: false)
        <section class="content-wrapper main-content clear-fix">
            @RenderBody()
        </section>
    </div>

    @RenderSection("scripts", required: false)
</body>
</html>

Add stylesheet 'CustomStyle.css' to improve the look and feel of the page.

[ng\:cloak], [ng-cloak], [data-ng-cloak], [x-ng-cloak], .ng-cloak, .x-ng-cloak {
    display: none !important;
}

body {
            margin: 20px;
            font-family: "Arial", "Helventica", sans-serif;
        }

        label {
            width: 80px;
            display: inline-block;
        }

        button {
            display: inline-block;
            outline: none;
            cursor: pointer;
            text-align: center;
            text-decoration: none;
            padding: .4em 1.1em .4em;
            color: #fef4e9;
            border: solid 1px #006fb9;
            background: #1276bb;
        }

            button:hover {
                text-decoration: none;
                background: #282828;
                border: solid 1px #000;
            }

        table {
            padding-top: 1em;
        }

        thead, tfoot {
            font-weight: 600;
        }

        th, td {
            padding: .1em .5em;
            text-align: left;
        }

            td li, td ul {
                margin: 0;
                padding: 0;
            }

            td li {
                display: inline;
            }

                td li::after {
                    content: ',';
                }

                td li:last-child::after {
                    content: '';
                }

Note

Here, I have defined style for ng-cloak directive used in our 'Product.cshtml'.
According to AngularJS Documentation.

The ngCloak directive is used to prevent the Angular html template from being briefly displayed by the browser in its raw (uncompiled) form while your application is loading. Use this directive to avoid the undesirable flicker effect caused by the html template display.

The reason for adding the class is because the ng-cloak directive is parsed after the html has been displayed, so there is always the possibility that your JS thread dies and still displays anything like {{something here}}

Now, change the default controller and action in Route.Config.cs.

routes.MapRoute(
              name: "Default",
              url: "{controller}/{action}/{id}",
              defaults: new { controller = "Product", action = "Product", id = UrlParameter.Optional }
           );

And also change the default routeTemplate to add action in WebApiConfig.cs.

config.Routes.MapHttpRoute(
               name: "DefaultApi",
               routeTemplate: "api/{controller}/{action}/{id}",
               defaults: new { id = RouteParameter.Optional }
           );

Hit Ctrl+F5.

That's it !!!



Congratulations!!! Now, you have successfully implemented CRUD operations in ASP.NET MVC 5 using WebAPI 2 using AngularJS.

Please comment for any queries.

Source Code

I have uploaded a sample project with SQL scripts, in case you need them. Don't forget to change the server name in ConnectionString of Web.Config.

Happy coding! :)

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

Indresh_Prajapati
Software Developer
India India
No Biography provided

You may also be interested in...

Pro
Pro

Comments and Discussions

 
QuestionNice tutorial but... Pin
Mike Hankey23-Jan-17 14:26
professionalMike Hankey23-Jan-17 14:26 
Question$scope.productsData.push is not a function Pin
Member 1290588115-Dec-16 1:11
memberMember 1290588115-Dec-16 1:11 
QuestionScripts SQL Pin
Member 128859194-Dec-16 8:29
memberMember 128859194-Dec-16 8:29 
GeneralNice Article Pin
ukeypunch4-Nov-16 7:48
memberukeypunch4-Nov-16 7:48 
Questiontable not get Pin
Member 1275392322-Sep-16 3:13
memberMember 1275392322-Sep-16 3:13 
QuestionRoute.config.cs issue Pin
omdurman196930-Aug-16 4:00
memberomdurman196930-Aug-16 4:00 
QuestionCRUD in ASP.NET MVC 5 using WebAPI 2 with AngularJS Pin
Member 126781699-Aug-16 16:15
memberMember 126781699-Aug-16 16:15 
QuestionAngular VS Knockout Pin
Paulo J Costa9-Aug-16 14:06
memberPaulo J Costa9-Aug-16 14:06 
Praisehelen Pin
Member 1233833429-Jul-16 2:24
memberMember 1233833429-Jul-16 2:24 
Questionproblem connectionstring?? Pin
Member 1264103818-Jul-16 21:54
memberMember 1264103818-Jul-16 21:54 
Questionexcellent Pin
rollie wi3-Jul-16 4:30
memberrollie wi3-Jul-16 4:30 
GeneralMy vote of 5 Pin
Santhakumar M22-Feb-16 5:17
professionalSanthakumar M22-Feb-16 5:17 
PraiseCool Pin
ezequielsd29-Jan-16 8:42
memberezequielsd29-Jan-16 8:42 
QuestionAny reason for not using $http Pin
Member 1056868828-Jan-16 1:30
memberMember 1056868828-Jan-16 1:30 
AnswerRe: Any reason for not using $http Pin
Indresh_Prajapati28-Jan-16 20:41
professionalIndresh_Prajapati28-Jan-16 20:41 
Generalgood post Pin
NIHS das27-Jan-16 22:11
memberNIHS das27-Jan-16 22:11 
QuestionSize of the download Pin
Mika Wendelius27-Jan-16 3:56
mvpMika Wendelius27-Jan-16 3:56 
AnswerRe: Size of the download Pin
Volkan Paksoy27-Jan-16 5:14
memberVolkan Paksoy27-Jan-16 5:14 
AnswerRe: Size of the download Pin
Indresh_Prajapati27-Jan-16 18:28
professionalIndresh_Prajapati27-Jan-16 18:28 
GeneralRe: Size of the download Pin
egg01022-Feb-16 11:21
memberegg01022-Feb-16 11:21 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

Permalink | Advertise | Privacy | Terms of Use | Mobile
Web02 | 2.8.170217.1 | Last Updated 22 Feb 2016
Article Copyright 2016 by Indresh_Prajapati
Everything else Copyright © CodeProject, 1999-2017
Layout: fixed | fluid