Create edit
in place table using AngularJS and ASP.NET MVC
How to use AngularJS in ASP.MVC project.
Hello
everybody,
Today I
will show you how to use AngularJS in your projects. I like AngularJS very much, so I will try to
explain a practical side of the using the framework, if you are not familiar
with a theory you can find it on AngularJS web site https://angularjs.org/ . So let’s begin.
The
Business logic of the project is simple. I want to show ordinary bank rates,
for example “Opening an account” in a table, where end user can just edit all
cell in a place.
We will
create a simple project for this demo lesson. Open Visual Studio and create a
new asp.net mvc project with the following structure as I did it.
<YourSolution>
<YourProject>
<YourProject>.Domain
<YourProject>.Tests
I prefer to use Empty template to create a
project, it allows me to control the project references and structure. As a
following step I need to install and AngularJS and jQuery frameworks into my
projects. I use NuGet commands to do this:
Install-Package
AngularJS.Core
Install-Package AngularJS.Route
Install-Package jQuery
Install-Package
EntityFramework
Install-Package
Angular.UI.Bootstrap
Install-Package modernizr
Install-Package bootstrap
Install-Package
Microsoft.AspNet.Web.Optimization
After
installing all necessary packages my Scripts folder looks like this
In folder Content create a new folder with name AngularJS. There we will put files for our angularjs Controller.js and Service.js
Create
these files Controller.js, Service.js and Module.js
Open Module.js file and add the following code snippet:
var myApp = angular.module('BankRatesApp', []);
var myApp = angular.module('BankRatesApp', []);
Open file _Layout.cshtml and add ng-app directive in html tag.
<html ng-app='BankRatesApp'>
Add the references for angular.js, angular-route.js, Module.js, Controller.js, Service.js as well.
<html ng-app='BankRatesApp'>
Add the references for angular.js, angular-route.js, Module.js, Controller.js, Service.js as well.
Listing 1-1 _Layout.cshtml
<!DOCTYPE html>
<html ng-app='BankRatesApp'>
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width,
initial-scale=1.0">
<title>@ViewBag.Title</title>
<script
src="~/Scripts/angular.js"></script>
<script
src="~/Scripts/angular.min.js"></script>
<script
src="~/Scripts/angular-route.js"></script>
<script
src="~/Content/Angular/Module.js"></script>
<script
src="~/Content/Angular/Service.js"></script>
<script
src="~/Content/Angular/Controller.js"></script>
@Styles.Render("~/Content/css")
</head>
<body>
<div>
@RenderBody()
</div>
@Scripts.Render("~/bundles/jquery")
@Scripts.Render("~/bundles/bootstrap")
@RenderSection("scripts",
required: false)
</body>
</html>
|
·
Add „Class Library” project to your solution
and name it <YourSolution>.Domain.
This project will serve as Domain Layer which will store our DbContext and
Models
·
Install EntityFramework into the project
·
Add reference System.Web.Mvc
·
Create folders Conrete and Entities
·
Add
Rate.cs class file into the folder
Entities
·
Modify
Rate.cs file according below Listing 1-2
Listing 1-2 Rate.cs file
using
System.ComponentModel;
using
System.ComponentModel.DataAnnotations;
using System.Web.Mvc;
namespace <YourSolution>.Domain.Entities
{
public class Rate
{
public int RateID { get; set; }
public string Name { get; set; }
public string Value { get; set; }
}
}
|
·
Add
EFDbContext.cs file into the folder Conrete
·
Save
Listing 1-3 into your EFDbContext.cs file
Listing 1-3 EFDbContext.cs
using <YourSolution>.Domain.Entities;
using
System.Data.Entity;
using
System.Data.Entity.ModelConfiguration.Conventions;
namespace <YourSolution>.Domain.Concrete
{
public class EFDbContext : DbContext
{
public EFDbContext()
: base("EFDbContext")
{
}
public DbSet<Rate> Rates { get; set; }
protected override void OnModelCreating(DbModelBuilder
modelBuilder)
{
modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();
}
}
}
|
·
Modify web.config file
<connectionStrings>
<add name="EFDbContext" connectionString="Data Source=(LocalDb)\v11.0;Initial
Catalog=BankRatesDB2015;Integrated Security=True" providerName="System.Data.SqlClient" />
</connectionStrings>
·
Add EFDbInitializer.cs
file into the folder Conrete
·
Save
Listing 1-4 into your EFDbInitializer.cs file
Listing 1-4 EFDbInitializer.cs
using System;
using
System.Collections.Generic;
using System.Linq;
using System.Text;
using
System.Threading.Tasks;
using <YourSolution>.Domain.Entities;
using
System.Data.Entity.Validation;
namespace <YourSolution>.Domain.Concrete
{
public class EFDbInitializer :
System.Data.Entity.DropCreateDatabaseIfModelChanges<EFDbContext>
{
protected override void Seed(EFDbContext
context)
{
var rateList = new List<Rate>
{
new Rate { Name = "Opening an account", Value = "10
EUR" },
new Rate { Name = "Account maintenance", Value = "5
EUR per year" },
new Rate { Name = "Maintenance of an inactive account", Value = "10
EUR per year" },
new Rate { Name = "Closing an account", Value = "Free
of charge" }
};
rateList.ForEach(s => context.Rates.Add(s));
try
{
context.SaveChanges();
}
catch (DbEntityValidationException e)
{
foreach (var eve in e.EntityValidationErrors)
{
string
name = eve.Entry.Entity.GetType().Name;
string
name2 = eve.Entry.State.ToString();
foreach
(var ve in eve.ValidationErrors)
{
string
pn = ve.PropertyName;
string
errM = ve.ErrorMessage;
}
}
}
}
}
}
|
Seed method will add 4 records into the table Rate.
In this example, we use abstract bank rates data from price list for private
customers. For example, it could be a price of 10 EUR for opening an account.
·
Add GenericRepository.cs
file into the folder Conrete
·
Add UnitOfWork.cs
file into the folder Conrete
·
Modify
GenericRepository.cs file according below Listing 1-5
Listing
1-5 EFDbInitializer.cs
using System;
using
System.Collections.Generic;
using System.Data;
using
System.Data.Entity;
using System.Linq;
using
System.Linq.Expressions;
namespace <YourSolution>.Domain.Concrete
{
public class GenericRepository<TEntity>
where
TEntity : class
{
internal EFDbContext
context;
internal DbSet<TEntity>
dbSet;
public GenericRepository(EFDbContext
context)
{
this.context =
context;
this.dbSet =
context.Set<TEntity>();
}
public virtual IEnumerable<TEntity>
Get()
{
IQueryable<TEntity>
query = dbSet;
return query.ToList();
}
public virtual TEntity GetByID(object id)
{
return dbSet.Find(id);
}
public virtual void Insert(TEntity
entity)
{
dbSet.Add(entity);
}
public virtual void Delete(object id)
{
TEntity entityToDelete = dbSet.Find(id);
Delete(entityToDelete);
}
public virtual void Delete(TEntity
entityToDelete)
{
if (context.Entry(entityToDelete).State
== EntityState.Detached)
{
dbSet.Attach(entityToDelete);
}
dbSet.Remove(entityToDelete);
}
public virtual void Update(TEntity
entityToUpdate)
{
dbSet.Attach(entityToUpdate);
context.Entry(entityToUpdate).State = EntityState.Modified;
}
}
}
|
·
Modify
UnitOfWork.cs file according below Listing 1-6
Listing
1-6 UnitOfWork.cs
using System;
using
System.Collections.Generic;
using System.Linq;
using System.Web;
using <YourSolution>.Domain.Entities;
namespace <YourSolution>.Domain.Concrete
{
public class UnitOfWork : IDisposable
{
private EFDbContext
context = new EFDbContext();
private GenericRepository<Rate>
rateRepository;
public GenericRepository<Rate>
RateRepository
{
get
{
if (this.rateRepository ==
null)
this.rateRepository
= new GenericRepository<Rate>(context);
return rateRepository;
}
}
public void Save()
{
context.SaveChanges();
}
private bool disposed = false;
protected virtual void Dispose(bool disposing)
{
if (disposed)
return;
if (disposing)
{
//Free any other managed objects here.
context.Dispose();
}
// Free any unmanaged objects here.
disposed = true;
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
}
}
|
·
Modify <YourProject> web.config add
in section <entityFramework>.... below
Listing 1-7
Listing 1-7 web.config
<contexts>
<context type="<YourProject>.Domain.Concrete.EFDbContext,
<YourProject>.Domain">
<databaseInitializer type="<YourProject>.Domain.Concrete.EFDbInitializer,
<YourProject>.Domain" />
</context>
</contexts>
Now
we need to create a controller and view for our model entity. Usually I create
a new controller for model class, but for this example, I will use Index.cshtml
under Home project and HomeController to simplify the example.
·
Open HomeController.cs file in Controllers folder and modify file
according Listing 1-8
Listing
1-8 HomeController.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using <YourSolution>.Domain.Concrete;
using <YourSolution>.Domain.Entities;
namespace <YourSolution>.Controllers
{
public class HomeController : Controller
{
UnitOfWork
unitOfWork = new UnitOfWork();
//
// GET: /Home/
public ActionResult
Index()
{
return View();
}
public JsonResult
GetRates()
{
List<Rate> ratesList =
unitOfWork.RateRepository.Get().ToList();
return Json(ratesList, JsonRequestBehavior.AllowGet);
}
}
}
|
Now if everything was done right, we can run the project
and see our action method has returned 4 records from local database table „Rate”
see pic.
We can see JSON string in browser as the result of action
GetRates
·
Open Service.js file and add the following
code snippet
this.getRate = function () {
return $http.get("/Home/GetRates");
};
·
Next open Index.cshtml file in View folder under Home.
·
Modify Index.cshtml according Listing 1-9
Listing
1-9 Index.cshtml
@{
Layout = "~/Views/Shared/_Layout.cshtml";
}
<div class="CSSTableGenerator" ng-controller="BankRatesCtrl">
<p>{{Operation}}</p>
<input type="button" value="Add" ng-click="add()" />
<table>
<tbody>
<tr>
<td>ID</td>
<td>Service</td>
<td>Price</td>
</tr>
<tr ng-repeat="rate in rates">
<td>
<div ng-hide="editingData[rate.RateID]">{{rate.RateID | uppercase}}</div>
<div ng-show="editingData[rate.RateID]" ng-model="rate.RateID">{{rate.RateID | uppercase}}</div>
</td>
<td>
<div ng-hide="editingData[rate.RateID]">{{rate.Name | uppercase}}</div>
<div ng-show="editingData[rate.RateID]"><input type="text" ng-model="rate.Name"
/></div>
</td>
<td>
<div ng-hide="editingData[rate.RateID]">{{rate.Value}}</div>
<div ng-show="editingData[rate.RateID]"><input type="text" ng-model="rate.Value"
/></div>
</td>
<td>
<button ng-hide="editingData[rate.RateID]" ng-click="edit(rate)">Edit</button>
<button ng-show="editingData[rate.RateID]" ng-click="save(rate)">Save</button>
<button ng-show="editingData[rate.RateID]" ng-click="delete(rate)">Remove</button>
</td>
</tr>
<tr ng-show="addNew">
<td>
<input type="text" style="width:20px;" disabled="disabled" ng-model="RateID" />
</td>
<td>
<input type="text" style="width:94px;" ng-model="rate.Name" />
</td>
<td>
<input type="text" style="width:94px;" ng-model="rate.Value" />
</td>
<td colspan="2">
<input type="button" value="Add
new" ng-click="save(rate)"
/>
</td>
</tr>
</tbody>
</table>
</div>
|
If you run application, you should
see the table as in picture below:
NOTE: I
have added all html and angulajs code at ones to save the time for entry code.
·
Open Service.js file and add the following
code snippet
//Save (Update)
this.update = function (rate) {
var response = $http({
method: "post",
url: "/Home/UpdateRates",
data: JSON.stringify(rate),
dataType: "json"
});
return response;
}
·
Open Controller.js file and add the
following code snippet
Function $scope.edit converts div eelemnts into the input texboxe with
values
$scope.edit
= function
(rate) {
$scope.editingData[rate.RateID] = true;
$scope.RateID = rate.RateID;
$scope.Name = rate.Name;
$scope.Value = rate.Value;
$scope.Operation = "Update";
$scope.divRateModification = true;
}
Function $scope.save creates an javacript object with values in row you
want yo edit
var Rate = {
RateID: rate.RateID,
Name: rate.Name,
Value: rate.Value
};
and pass it to Service.js function called angularService.update(Rate). Action method in
HomeConstoller.cs file is invoked. The method gets the record by id, modifies
the values of an existiong object and saves the changes in the table of
database.
$scope.save
= function
(rate) {
var Rate = {
RateID: rate.RateID,
Name: rate.Name,
Value: rate.Value
};
var Operation = $scope.Operation;
if (Operation == "Update") {
alert('dd');
var getMSG = angularService.update(Rate);
getMSG.then(function
(messagefromController) {
GetAllRates();
alert(messagefromController.data);
$scope.divRateModification = false;
}, function () {
alert('Update Error');
});
}
else {
//Add
method
}
$scope.editingData[rate.RateID] = false;
}
·
Open HomeController.cs file in Controllers folder and add
the following code according Listing 1-10
Listing 1-10 HomeController.cs add
UpdateRates action method
public string UpdateRates(Rate rate) {
if (rate != null)
{
var rateRecord =
unitOfWork.RateRepository.GetByID(rate.RateID);
if (rateRecord != null)
{
rateRecord.Name = rate.Name;
rateRecord.Value = rate.Value;
unitOfWork.RateRepository.Update(rateRecord);
unitOfWork.Save();
return "Record has been
Updated";
}
else
return
"Update error";
}
else
{
return "Record has Not been Updated";
}
}
|
·
Now if you click on “Edit” button the row cells should
be changed to input elements as it shown in the picture
·
Change the price for first record and click
button Save. You should see the
Dialog box with message “Record has been Updated” and the cell value should
have a new value
·
Now we will add Delete function to our solution
·
Open Service.js file and add the following
code snippet
//Delete
this.Delete = function (rateID) {
var response = $http({
method: "post",
url: "/Home/Delete",
params: {
id: rateID
}
});
return response;
}
·
Open HomeController.cs file in Controllers folder and add
the following code for delete method
according Listing 1-11
Listing
1-11 HomeController.cs
public string Delete(int id)
{
try
{
if (id != 0)
{
unitOfWork.RateRepository.Delete(id);
unitOfWork.Save();
return "Rate has Been Deleted";
}
else
{
return "Rate has not Been Deleted";
}
}
catch
{
return "Rate has not Been
Deleted";
}
}
·
Open Controller.js file and add the
following code snippet
$scope.delete = function (rate) {
alert(rate.RateID);
var getMSG = angularService.Delete(rate.RateID);
getMSG.then(function
(messagefromController) {
GetAllRates();
alert(messagefromController.data);
}, function () {
alert('Delete Error');
});
}
·
Run the project, click Edit button and then click Remove.
The record “Opening an account” should be deleted.
·
The last action we will add to this demo example
is adding a new record.
·
Open Controller.js file, replace comments
//Add method with the
following code snippet
var getMSG
= angularService.Add(Rate);
getMSG.then(function (messagefromController) {
GetAllRates();
alert(messagefromController.data);
$scope.divRateModification = false;
$scope.addNew = false;
}, function () {
alert('Insert Error');
});
}
·
Open Service.js file and add the following
code snippet
//Add
this.Add = function (rate) {
var response = $http({
method: "post",
url: "/Home/Add",
data: JSON.stringify(rate),
dataType: "json"
});
return response;
}
·
Open HomeController.cs file in Controllers folder and add
the following code for add method according
Listing 1-12
public string Add(Rate rate)
{
try
{
if (rate != null)
{
unitOfWork.RateRepository.Insert(rate);
unitOfWork.Save();
return "Record has been Added";
}
else
{
return "Record has Not been Verified";
}
}
catch
{
return "Record has Not been
Added";
}
}
A
new record „NEW SERVICE” with price „10 EUR” was added to database
In this step by step tutorail I have tired to show you how
to make edit in place table to add, update and delete records from database
using AngularJS framework in ASP.MVC project. The project programming code you
can find going to the following link on GitHub.
I hope you enjoy the demo and it will help you. If you find
any inaccuracies in the
article, please let me know.
Have a good
day! J
//Best regards, Aleks
No comments:
Post a Comment