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()
{
return ProductDB.TblProductLists;
}
public TblProductList Get(int id)
{
return ProductDB.TblProductLists.Find(id);
}
public TblProductList Add(TblProductList item)
{
if (item == null)
{
throw new ArgumentNullException("item");
}
ProductDB.TblProductLists.Add(item);
ProductDB.SaveChanges();
return item;
}
public bool Update(TblProductList item)
{
if (item == null)
{
throw new ArgumentNullException("item");
}
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)
{
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:'₹':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:'₹':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.
var app = angular.module('demoModule', []);
app.controller('demoCtrl', function ($scope, $http, ProductsService) {
$scope.productsData = null;
ProductsService.GetAllRecords().then(function (d) {
$scope.productsData = d.data;
}, function () {
alert('Error Occured !!!');
});
$scope.total = function () {
var total = 0;
angular.forEach($scope.productsData, function (item) {
total += item.Price;
})
return total;
}
$scope.Product = {
Id: '',
Name: '',
Price: '',
Category: ''
};
$scope.clear = function () {
$scope.Product.Id = '';
$scope.Product.Name = '';
$scope.Product.Price = '';
$scope.Product.Category = '';
}
$scope.save = function () {
if ($scope.Product.Name != "" &&
$scope.Product.Price != "" && $scope.Product.Category != "") {
$http({
method: 'POST',
url: 'api/Product/PostProduct/',
data: $scope.Product
}).then(function successCallback(response) {
$scope.productsData.push(response.data);
$scope.clear();
alert("Product Added Successfully !!!");
}, function errorCallback(response) {
alert("Error : " + response.data.ExceptionMessage);
});
}
else {
alert('Please Enter All the Values !!');
}
};
$scope.edit = function (data) {
$scope.Product = { Id: data.Id, Name: data.Name, Price: data.Price, Category: data.Category };
}
$scope.cancel = function () {
$scope.clear();
}
$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 !!');
}
};
$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);
});
};
});
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! :)