Table of Contents
AngularJS is a structural JavaScript framework to build a Single Page Application (SPA). AngularJS is a complete client-side framework which provides almost all the services such as data binding, templating, routing, validation, testing, dependency injection and resource for CRUD operations, etc.
AngularJS uses MVC design pattern to structure the code. In MVC, data and code are separate in three components, Model, View and Controller.
Model contains the application data. For example:
var products = [
{'name': 'Product 1',
'description': 'Description of Product 1'},
{'name': 'Product 2',
'description': 'Description of Product 2'},
{'name': 'Product 3',
'description': 'The 'Description of Product 3'}
];
View contains the HTML with declarative data binding:
<ul>
<li ng-repeat="product in products">
<span>{{product.name}}</span>
<p>{{product.description}}</p>
</li>
</ul>
Controller is used as a mediator between Model and View. It contains data model and event handler. For example:
productApp.controller('ProductListCtrl', function ($scope) {
$scope.price=100;
$scope.AddProduct= function(product)
{
};
});
One of the powerful features of AngularJS is two way data binding. In two way data binding, if user changes the UI value, then AngularJS automatically updates the underlying data model. In the reverse way, if underlying data model is changed, then Angular automatically updates the UI. It reduces the code writing to update the DOM.
The following picture shows two way data binding:
Using Routing, we can partition the Index page in multiple view templates and can bookmark the URL in browser for different view template. For routing, Angular uses its built in "ngRoute
" module and $routeProvider provider. The configuration for routing for the sample project is shown below:
var productcatApp = angular.module('productcatApp', [
'ngRoute',
'productcatControllers',
'productcatServices'
]);
productcatApp.config(['$routeProvider',
function ($routeProvider) {
$routeProvider.
when('/products', {
templateUrl: 'Home/ProductList',
controller: 'ProductListCtrl'
}).
when('/products/:productId', {
templateUrl: 'Home/ProductDetail',
controller: 'ProductDetailCtrl'
}).
when('/productNew', {
templateUrl: 'Home/ProductNew',
controller: 'ProductNewCtrl'
}).
when('/productUpdate/:productId', {
templateUrl: 'Home/ProductUpdate',
controller: 'ProductUpdateCtrl'
}).
otherwise({
redirectTo: '/products'
});
}]);
For every route, there is a Web API URL to extract view template and a controller to bind data with the extracted template.
Description: This sample project is about how to display, add, edit and delete product from a product list.
The database diagram from this sample project is shown below:
Index Page
Index page is used like a master page and contains a container which is always updated with the called template.
<div id="contentWrapper">
<div class="container">
<div ng-view></div>
</div>
</div>
When user routes to a new url, then the content of the div
with Angular directive ng-view
is replaced with the new HTML template.
The sample project performs the following steps.
Step 1: Shows the List of Products
The HTML template to display the list of products is shown below. File ProductList.cshtml contains this template in server.
<div class="row">
<div class="col-xs-6 col-sm-6 col-md-6 col-lg-6">
<button class="btn btn-primary btn-lg btn-block" ng-click="newProduct()">New</button>
</div>
</div>
<div class="row" ng-repeat="product in products">
<div class="col-xs-6 col-sm-8 col-md-8 col-lg-8">
<a href="#/products/{{product.id}}">{{product.name}}</a>
</div>
<div class="hidden-xs col-sm-1 col-md-1 col-lg-1">
<a href="#/products/{{product.id}}"><img class="img-thumbnail img-responsive"
ng-src="{{product.imageUrl}}"/></a>
</div>
<div class="col-xs-3 col-sm-2 col-md-2 col-lg-2">
<a class="btn btn-large btn-success" href="#/productUpdate/{{product.id}}">Update</a>
</div>
<div class="col-xs-3 col-sm-2 col-md-2 col-lg-2">
<button class="btn btn-large btn-success"
ng-click="deleteProduct(product)">Delete</button>
</div>
</div>
In the above template, ng-repeat="product in products"
is used to loop the products
list and {{product.name}}
syntax is used for two way data binding. And class="col-xs-6 col-sm-6 col-md-6 col-lg-6"
syntax is used for bootstrap.
The Routing part to call this template is:
when('/products', {
templateUrl: 'Home/ProductList',
controller: 'ProductListCtrl'
})
When url http://localhost/#/products is requested, then Angular calls an Ajax request to the Web API URL 'Home/ProductList' for a new HTML template and after return Angular bound the Controller 'ProductListCtrl
' with the new template for data.
The Controller for this Template is:
var productcatControllers = angular.module('productcatControllers', []);
productcatControllers.controller('ProductListCtrl', ['$scope', '$routeParams', '$location',
'$route', 'productService',
function ($scope, $routeParams, $location, $route, productService) {
productService.query(function (data) {
$scope.products = data;
});
$scope.deleteProduct = function (product) {
debugger;
productService.delete({ id: product.id }, function () {
$route.reload();
});
}
$scope.newProduct = function () {
$location.path('/productNew');
}
}]);
Here, Angular Resource module is used to extract data from server and delete a product from server. Here, newProduct()
function is used to redirect to the New Product add page.
The configuration for Resource module is:
var phonecatServices = angular.module('productcatServices', ['ngResource']);
phonecatServices.factory("productService", function ($resource) {
return $resource(
"/api/Product/:id",
{ id: "@id" },
{
"update": { method: "PUT" }
}
);
});
The above statement creates a service with name productService
and passes a $resource
service as a parameter which is used for database operations.
The server side Web API code is:
public HttpResponseMessage GetProducts()
{
var collection = db.Products.Select(x => new
{
id = x.id,
name = x.name,
imageUrl = x.imageUrl
});
var yourJson = new JavaScriptSerializer().Serialize(collection);
var response = this.Request.CreateResponse(HttpStatusCode.OK);
response.Content = new StringContent(yourJson, Encoding.UTF8, "application/json");
return response;
}
Step 2: Add a New Product
ProductNew.cshtml page in server contains HTML template for this UI.
The Routing part to call this template is:
when('/productNew', {
templateUrl: 'Home/ProductNew',
controller: 'ProductNewCtrl'
})
The Controller for this Template is:
productcatControllers.controller('ProductNewCtrl', ['$scope', '$routeParams', '$location',
'productService',
function ($scope, $routeParams, $location, productService) {
$scope.product = { name: "", imageUrl: "", ProductDetail: { number: 0, price: 0.0,
description: "", companyName: "" } };
$scope.addNewProduct = function (product) {
productService.save(product, function () {
$location.path('/products');
});
}
$scope.cancelNewProduct = function () {
$location.path('/products');
}
}]);
In the about controller, save function of productService
service is used to save a product in database and after save, it redirects to the product list page.
The server side Web API code is:
public HttpResponseMessage PostProduct(Product product)
{
if (ModelState.IsValid)
{
db.Products.Add(product);
db.ProductDetails.Add(product.ProductDetail);
db.SaveChanges();
return Request.CreateResponse(HttpStatusCode.OK);
}
else
{
return Request.CreateErrorResponse(HttpStatusCode.BadRequest, ModelState);
}
}
Step 3: Edit a Product
ProductUpdate.cshtml page in server contains HTML template for this UI.
The Routing part to call this template is:
when('/productUpdate/:productId', {
templateUrl: 'Home/ProductUpdate',
controller: 'ProductUpdateCtrl'
})
The Controller for this Template is:
productcatControllers.controller('ProductUpdateCtrl', ['$scope', '$routeParams',
'$location', 'productService',
function ($scope, $routeParams, $location, productService) {
productService.get({ id: $routeParams.productId }, function (data) {
$scope.product = data;
});
$scope.updateProduct = function (product) {
var post = productService.get({}, { id: product.id }, function (data) {
debugger;
post.name = product.name;
post.imageUrl = product.imageUrl;
post.ProductDetail.number = product.ProductDetail.number;
post.ProductDetail.price = product.ProductDetail.price;
post.ProductDetail.description = product.ProductDetail.description;
post.ProductDetail.companyName = product.ProductDetail.companyName;
productService.update(post, function () {
$location.path('/products');
});
});
}
$scope.cancelUpdateProduct = function () {
$location.path('/products');
}
}]);
In the about controller, update
function of productService
service is used to update a product
in database and after update it redirect to the product list page.
The server side Web API code is:
public HttpResponseMessage PutProduct(int id, Product product)
{
if (!ModelState.IsValid)
{
return Request.CreateErrorResponse(HttpStatusCode.BadRequest, ModelState);
}
if (id != product.id)
{
return Request.CreateResponse(HttpStatusCode.BadRequest);
}
Product productEntity =
db.Products.Where(p => p.id == product.id).FirstOrDefault();
productEntity.name = product.name;
productEntity.imageUrl = product.imageUrl;
productEntity.ProductDetail.number = product.ProductDetail.number;
productEntity.ProductDetail.price = product.ProductDetail.price;
productEntity.ProductDetail.description=product.ProductDetail.description;
productEntity.ProductDetail.companyName = product.ProductDetail.companyName;
db.Entry(productEntity).State = EntityState.Modified;
try
{
db.SaveChanges();
}
catch (DbUpdateConcurrencyException ex)
{
return Request.CreateErrorResponse(HttpStatusCode.NotFound, ex);
}
return Request.CreateResponse(HttpStatusCode.OK);
}
Step 4: Display the Product Details
ProductDetail.cshtml page in server contains HTML template for this UI.
The Routing part to call this template is:
when('/products/:productId', {
templateUrl: 'Home/ProductDetail',
controller: 'ProductDetailCtrl'
})
The Controller
for this Template is:
productcatControllers.controller('ProductDetailCtrl', ['$scope', '$routeParams',
'$location', 'productService',
function ($scope, $routeParams, $location, productService) {
productService.get({ id: $routeParams.productId }, function (data) {
$scope.product = data;
});
$scope.backToProduct = function () {
$location.path('/products');
}
}]);
In the about controller, get
function of productService
service is used to extract a product by id from database and assign the data with product Model
.
The server side Web API code is:
public HttpResponseMessage GetProduct(int id)
{
var product = db.Products.Include("ProductDetail").Where(p => p.id == id).Select(x => new
{
id = x.id,
name = x.name,
imageUrl = x.imageUrl,
ProductDetail = new { id = x.ProductDetail.id, number = x.ProductDetail.number,
price = x.ProductDetail.price,
description = x.ProductDetail.description,
companyName = x.ProductDetail.companyName }
}).FirstOrDefault();
var json = new JavaScriptSerializer().Serialize(product);
var response = this.Request.CreateResponse(HttpStatusCode.OK);
response.Content = new StringContent(json, Encoding.UTF8, "application/json");
return response;
}
In conclusion, I can say, now a days, Angular is a popular JavaScript framework to develop a Single Page Application. Please download the attached zip file for the sample project. It needs VS2012 and SQL Server 2008 to run.
- 21st November, 2014: Initial version