Introduction
The aim of this article is to clear AngularJS technology and Repository which is one of the useful design patterns in order to develop web applications easily and in a more professional manner. There are some fundamental difficulties with the Jquery such as sticking among unstructured code or tangling in deadlocks but AngularJS is able to solve some barriers in Jquery. AngularJS is enough power, specially for CRUD (Create, Read, Update and Delete) web applications, it follows MVC pattern, so our code is neat and clear. Besides, it enables us to write less code and testing code has better condition in AngularJS. Albeit these factors do not diminish Jquery power for writing the other types of applications such as Game, etc.
The other major thing in this article is to explain repository pattern which encapsulates data access layer and draw a separate line between the actual database and other parts of the data access layer. It has some advantages for developers, for example, it makes changing and updating data context more convenient and testing frameworks do not run for physical data access code, so this isolation makes it more flexible.
Last but not least is how to debug scripts in web browser for beginners.
I will describe more about each of the sections widely, I will open these technologies and patterns within a real project in order to have a clear concept from them.
Background
What is Repository
Repository is one of the useful design patterns which hides physical data from the other sections of data access layers. Professionally speaking, Repository gives us an instruction on how to encapsulate our data context from other database queries and data access logic. It is such as a line which isolates actual database from other parts, eventually, it makes our web application more convenient to test layer by layer. Moreover, it supplies an in memory like collection http://www.codeproject.com/Articles/832189/List-vs-IEnumerable-vs-IQueryable-vs-ICollection-v for accessing domain objects which ignore persistence and enhance performance. So we prefer to use repository in MVC application to have test ability and speed with the flexibility to change in the future.
We have two choices for repository structure. One is building one Repository
class and IRepository
interface for each entity and another way is creating one GenericRepository
class for all entities and defining basic CRUD (Create
, Read
, Update
, Delete
) operations and one UnitOfWork
class so that all repositories for each entity should be built on UnitOfWork
class. Definitely, the second way is approved when having too many entities.
As we make for each entity one controller in MVC as a “XController
”, we create a folder and name it GenericRepository and make one class “GenericRepository.cs”. Class “GenericRepository.cs” has CRUD functions deal with data context and then create a folder and name it "UnitOfWork
" and make one class “UnitOfWork.cs”. You should write inside "UnitOfWork.cs" list of repositories for each entity and moreover "Save()
" and "Dispose()
" methods. Controllers just have access to UnitOfWork
. Therefore, you separated your business layer from data layer and whenever you want to change your policy and prefer to use another ORM such as NHibernate, you will not have any trouble with high layer such as business layer.
Why Repository with Entity Framework
It is good question that if we use Entity Framework so why do we need to establish another repository above Data Context. Because Data Context is repository itself. Imagine one day, you want to use in Data Access Layer other approaches such as NHibernate, ADO.NET or even XML files or suppose you have to change database from SQL to Oracle, so your trouble is very small and you have to change just a bit in your code instead of changing almost the whole of your structure and write it again. Therefore Repository Pattern enables us to decouple layers properly and comes in handy when we will change our section especially data layer in future. On the other hand, testing will be easy.
What is AngularJS
AngularJS is a JavaScript framework for organizing HTML code in a more structural, clear and succinct mode. This client-side technology provides easy and light code in order to make developing and even designing as a simple task. AngularJS eliminates most of the redundant code especially in CRUD web application. It is a MV (whatever you like) pattern and not just an MVC.
There are two kinds of accessories which help us to make a bridge between user interface and the core of business and data layer.
- Library: These types are set of functions such as JQuery and whenever you use some of them (
$.ajax
), it will call related function to handle functions. - Frameworks: These types are a specific structure for implementation application and developers have to follow these structures such as durandal and ember.
AngularJS has a different approach to work out our needs, Angular embeds new properties inside HTML tags such as “ng-model
”, “ng-repeat
” which are known as Directives in order to fade our barriers to match and transform data.
On the other hand, AngularJS has mingled Ajax and HTML DOM in a good structure which I will describe more as follows:
Angular Advantages
- It is very good for CRUD web application.
- It makes testing easy in CRUD web application.
- You need to write less code.
- You can run your code more quickly.
- One of the most popular and famous features in the AngularJS is two way and simple binding:
<input type="text" ng-model="simpleBinding" /><span>{{simpleBinding}}</span>
- You do not need to modify HTML DOM.
- With the help of directives. your tasks are more easy and time saving.
- By its architecture in MVC or in the better word MVW, decoupling is real and excellent.
Design Guidance
As you see in the above picture, you should download Angular files from https://angularjs.org/ and put jquery and Angular js files inside script folder. Inside Module.js, write name for angular app such as "MyApp
" - this name relates your Angular file to each other and you should use it inside html
tag for your view, in this scenario, I have used "_Layout.cshtml" as my master view (according to MVC Pattern), in this html
tag, you should write a directive angular ng-app='MyApp'
which holds Angular files.
Then you have to introduce these files into your view by <script src=?>
.
Index.cshtml inherits from _Layout.cshtml, now by adding another directive as ng-controller='angularCtrl'
inside a div
, you make a relation between your view and controller.js because angularCtrl
is name of Controller.js.
You just need to use a simple HTML tag such as <input type='text'>
and add ng-model='empid'
directive to this input
tag and whenever you want to refer to this input (set or get data) from Controller.js, call it by $Scope.empid
.
This same story repeats for <input type='button'>
and add ng-click='add()'
directive to this input
tag and whenever you want to refer to this input (set or get data) from Controller.js, call it by $Scope.add()
.
For representing data inside table in Angular, you can use simple table
tag with the help of ng-repeat="employee in employees"
now whenever you want to (set or get data) from Controller.js, you just need to use: $Scope.employees
and use expressions such as {{employee.ID}}
or {{employee.FirstName}}
or other fields.
<table>
<tr ng-repeat="employee in employees">
<td>
{{employee.ID}}
</td>
</tr>
</table>
If you want to write function inside controller.js, you should call ServiceName.FunctionName
such as angularservice.getEmp(), "angularservice"
is the name of Service.js. Now in the "Service.js", you can continue function { "getEmp()" }
with $http.get("/ControllerName/ActionName")
, or for adding
function which you use to pass data from view to controller use:
Inside Controller.js to pass data to service.
var Employee = {
FirstName: $scope.FirstName ,
LastName: $scope.LastName ,
UserName: $scope.UserName
};
angularService.Add(Employee)
Inside Service.js to pass data to EmployeeController.cs.
this.Add = function (employee) {
var response = $http({
method: "post",
url: "/Employee/Add",
data: JSON.stringify(employee),
dataType: "json"
});
return response;
}
Angularjs vs Jquery
Barriers in the Jquery and Solution in AngularJS:
- Jquery Problem: If you want to use multiple entities, you are not allowed to use multiple models in one view, therefore you have to write
viewmodel
to support it.
AngularJS Solution: You can handle it by using $Scope
object. This tiny and awesome object can supply data and make changes.
- Jquery Problem: You have problems using
foreach
loop if you are going to use viewmodel
, of that you have to use for
loop.
AngularJS Solution: You can solve it by using ng-repeat
directive inside table
{tr
tag}.
- Jquery Problem: You should manipulate on HTM DOM and customized your requirement according to your model architecture and writing something like:
Html.EditorFor(model => model.ArticleTitle)
AngularJS Solution: But with the help of AngularJS, you can use <input>
and just by adding specific directives, you will reach your goal.
How to Arrange AngularJS Files
Dispose Pattern
In order to know Dispose
pattern, it is necessary to know Garbage Collection.
Garbage Collection
When you create object from data type, Common Language Runtime (CRL) assigns specific space inside memory for your new object, in this process, Managed Heap helps to CLR to allocate object to memory. Memory space is finite and it is not possible that many objects as consumers use resources. Indeed, we need a tool to release memory from objects which do not use memory for a long time and they were not called by applications for a while. Garbage Collector is a proper tool to manage memory and release space from old and useless objects.
IDisposable
is an interface to release resources for particular reclaiming resources whose life time is well known to a programmer. Its advantages are high speed allocation service without long term fragmentation problems.
In the below picture, you see three stages, first one objects with these names "A", "B", "C" have taken resources as it is obvious, "A" and "B" are dead whereas "C" is live, on the other hand, our new objects "D", "E" and "F" are in the queue to use resources. Suppose there are no tools to stop and release memory or resources so the other objects must wait long in the queue and the performance comes down. Garbage Collection comes here to help us and gather "A" and "B" and takes them from resource because they are old and no application uses them in the long term. So in the second stage, we have more free space and "D", "E" and "F" will come in the third stage. Dispose method or in a better words "Dispose Pattern" helps us to have more performance, especially when we know the life time of the resource (context).
class X: IDisposable
{
bool disposed = false;
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing)
{
if (disposed)
return;
if (disposing) {
context.Dispose();
}
disposed = true;
}
~X()
{
Dispose(false);
}
};
protected override void Dispose(bool disposing)
{
X.Dispose();
base.Dispose(disposing);
}
How to Install AngularJS
Go to -> Tools Menu -> Library Package Manager -> Package Manager Console
Write in command line: Install-Package angularjs -Version 1.3.8
Be careful about variant Angular version as it might need a slightly different code style.
Using the Code
Scenario
I want to follow permission management scenario to better explain these concepts (AngularJS and Repository in MVC). It includes two pages, first on just do CRUD functions, creating employees, reading or selecting employees, updating and deleting their information, besides them, it mentions to other facilities in Angular such as routing, filtering. Second page assigns permission for each of the employees, here you need to have a combo box to keep employees name and permission roles. Therefore, it covers fundamental requirements in dealing with having a robust MVC web application.
- Employee CRUD (Create Read Update Delete): This page shows data and user can edit and delete or add new
employee
to it.
- AngularJS is able to filter data:
- When user clicks on "Edit" button -> "Update Employee" section appears.
- When user clicks on "Add" button -> "Add Employee" section appears. Finally, by clicking on "Permission" link, Angular leads you to permission page by routing facility.
- User can assign permission to specific
employee
by selecting his or her name from combobox
:
- Loading data inside
combobox
:
Step By Step Using Repository and AngularJS in your MVC Project
(Instruction as Cookbook)
- Create database and name it "
MVCAdvanced
". - Create two Tables: "
Employee
" and "EmpRole
"
- Create New MVC Project.
- Create New Data Model.
- Create New Folder in Solution and name it "
GenericRepository
". - Create "GenericRepository.cs" Class:
using System;
using System.Collections.Generic;
using System.Data;
using System.Data.Entity;
using System.Linq;
using System.Linq.Expressions;
namespace MvcAdvanced.GenericRepository
{
public class GenericRepository<tentity> where TEntity : class
{
internal MvcAdvancedEntities context;
internal DbSet<tentity> dbSet;
public GenericRepository(MvcAdvancedEntities 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;
}
}
}
- Create "
UnitOfWork
" folder and create a class "UnitOfWork.cs":
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using MvcAdvanced.GenericRepository;
namespace MvcAdvanced.UnitOfWork
{
public class UnitOfWork : IDisposable
{
private MvcAdvancedEntities context = new MvcAdvancedEntities();
private GenericRepository<employee> employeeRepository;
private GenericRepository<emprole> empRoleRepository;
public GenericRepository<employee> EmployeeRepository
{
get
{
if (this.employeeRepository == null)
this.employeeRepository = new GenericRepository<employee>(context);
return employeeRepository;
}
}
public GenericRepository<emprole> EmpRoleRepository
{
get
{
if (this.empRoleRepository == null)
this.empRoleRepository = new GenericRepository<emprole>(context);
return empRoleRepository;
}
}
public void Save()
{
context.SaveChanges();
}
private bool disposed = false;
protected virtual void Dispose(bool disposing)
{
if (disposed)
return;
if (disposing)
{
context.Dispose();
}
disposed = true;
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
}
}
- Create "
EmployeeController
" inside "Controller" folder:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using MvcAdvanced.GenericRepository;
using AutoMapper;
namespace MvcAdvanced.Controllers
{
public class EmployeeController : Controller
{
private UnitOfWork.UnitOfWork unitOfWork = new UnitOfWork.UnitOfWork();
public ActionResult Index()
{
return View();
}
public JsonResult GetAllEmployees()
{
var employeeList = (List<employee>)unitOfWork.EmployeeRepository.Get();
return Json(employeeList, JsonRequestBehavior.AllowGet);
}
public string Update(Employee employee)
{
if (employee != null)
{
var emp = unitOfWork.EmployeeRepository.GetByID(employee.ID);
emp.FirstName = employee.FirstName;
emp.LastName = employee.LastName;
emp.UserName = employee.UserName;
unitOfWork.EmployeeRepository.Update(emp);
unitOfWork.Save();
return "Record has been Updated";
}
else
{
return "Record has Not been Updated";
}
}
public string Delete(int id)
{
try
{
if (id != null)
{
unitOfWork.EmployeeRepository.Delete(id);
unitOfWork.Save();
return "Employee Has Been Deleted";
}
else
{
return "Employee Hasnot Been Deleted";
}
}
catch
{
return "Employee Hasnot Been Deleted";
}
}
public string Add(Employee employee)
{
try
{
if (employee != null)
{
unitOfWork.EmployeeRepository.Insert(employee);
unitOfWork.Save();
return "Record has been Added";
}
else
{
return "Record has Not been Verified";
}
}
catch
{
return "Record has Not been Added";
}
}
protected override void Dispose(bool disposing)
{
unitOfWork.Dispose();
base.Dispose(disposing);
}
}
}
- Create "
PermissionController
" inside "Controller
" Class:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using MvcAdvanced.GenericRepository;
namespace MvcAdvanced.Controllers
{
public class PermissionController : Controller
{
private UnitOfWork.UnitOfWork unitOfWork = new UnitOfWork.UnitOfWork();
public ActionResult Index()
{
return View();
}
public JsonResult GetAllEmpRole()
{
var employeeList = unitOfWork.EmpRoleRepository.Get();
return Json(employeeList, JsonRequestBehavior.AllowGet);
}
public JsonResult GetAllEmpNames()
{
var employeeList = unitOfWork.EmployeeRepository.Get();
return Json(employeeList, JsonRequestBehavior.AllowGet);
}
public JsonResult GetAllRoles()
{
var roleList = (from emp in unitOfWork.EmployeeRepository.Get() join role
in unitOfWork.EmpRoleRepository.Get() on emp.RoleID equals role.ID
select new { emp.ID, emp.FirstName,
emp.LastName, emp.UserName, role.Role }).ToList();
return Json(roleList, JsonRequestBehavior.AllowGet);
}
public string UpdateRole(Employee permission)
{
if (permission != null)
{
var emp = unitOfWork.EmployeeRepository.GetByID(permission.ID);
emp.RoleID = permission.RoleID;
unitOfWork.EmployeeRepository.Update(emp);
unitOfWork.Save();
return "Record has been Updated";
}
else
{
return "Record has Not been Updated";
}
}
protected override void Dispose(bool disposing)
{
unitOfWork.Dispose();
base.Dispose(disposing);
}
}
}
- Downloading angularjs from:
I have used angularjs version 1.3.8. Something (code style) might be changed version by version. Put angular.js and angular-route.js inside "Scripts" folder.
- Create Angular Files:
In the "Content" folder, make new folder and name it "Angular", then create three JavaScript files inside it and name them: { Module.js, Controller.js, Service.js }
- Inside Module.js, write:
var app = angular.module('MyApp', ['ngRoute']);
app.config(['$routeProvider',function ($routeprovider) {
$routeprovider.
when('/employee', {
templateurl: '/Views/Employee/index.cshtml',
controller: 'AngularCtrl'
}).
when('/permission', {
templateurl: '/Views/Permission/index.cshtml',
controller: 'AngularCtrlRole'
}).
otherwise({
redirectto: '/employee'
});
}]);
- Inside Views -> Shared -> _Layout.cshtml, write: (you are introducing your application name "
MyApp
" to your view by adding ng-app
):
<!DOCTYPE html>
<html ng-app='MyApp'>
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width" />
<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>
@RenderBody()
@Scripts.Render("~/bundles/jquery")
@RenderSection("scripts", required: false)
</body>
</html>
- Inside Views -> Employee -> Index.cshtml, write:
@{
ViewBag.Title = "Index";
}
<h2>Index</h2>
<div ng-controller="AngularCtrl">
<a href="/permission">Permission</a><br />
Filter: <input type="text" value="Name" ng-model="FilterByFirstName" />
<br />
<br />
<input type="button" value="Add" ng-click="add()" />
<table cellpadding="12">
<tr>
<td><b>ID</b></td>
<td><b>FirstName</b></td>
<td><b>LastName</b></td>
<td><b>UserName</b></td>
<td><b>Operation</b></td>
</tr>
<tr ng-repeat="employee in employees | filter:FilterByFirstName ">
<td>
{{employee.ID}}
</td>
<td>
{{employee.FirstName}}
</td>
<td>
{{employee.LastName}}
</td>
<td>
{{employee.UserName}}
</td>
<td>
<input type="button" value="Edit" ng-click="edit(employee)" />
<input type="button" value="Delete" ng-click="delete(employee)" />
</td>
</tr>
</table>
<div ng-show="divEmpModification">
<p>{{Operation}} Employee</p>
<table>
<tr>
<td>
<input type="text" style="width:20px;"
disabled="disabled" ng-model="ID" />
</td>
<td>
<input type="text" style="width:94px;" ng-model="FirstName" />
</td>
<td>
<input type="text" style="width:94px;" ng-model="LastName" />
</td>
<td>
<input type="text" style="width:94px;" ng-model="UserName" />
</td>
<td colspan="2">
<input type="button" value="Save" ng-click="Save()" />
</td>
</tr>
</table>
</div>
</div>
- Inside Views -> Permission -> Index.cshtml write:
@{
ViewBag.Title = "Index";
}
<h2>Index</h2>
<div ng-controller="AngularCtrlRole">
<a href="/employee">Employee</a>
<p ng-model="mahsa"> ID of selected LastName is : {{selectedItem.ID}} </p>
<select data-ng-Model="selectedItem"
ng-options="item.LastName for item in items">
</select>
<p> ID of selected Role is : {{selectedItemRole.ID}} </p>
<select ng-model="selectedItemRole"
ng-options="roleitem.Role for roleitem in roleitems">
</select>
<input type="button" value="Save Permission" ng-click="SavePermission()" />
<table cellpadding="12">
<tr>
<td><b>ID</b></td>
<td><b>First Name</b></td>
<td><b>Last Name</b></td>
<td><b>User Name</b></td>
<td><b>Role Name</b></td>
</tr>
<tr ng-repeat="view in views ">
<td>
{{view.ID}}
</td>
<td>
{{view.FirstName}}
</td>
<td>
{{view.LastName}}
</td>
<td>
{{view.UserName}}
</td>
<td>
{{view.Role}}
</td>
</tr>
</table>
</div>
- Inside Content -> Angular -> Controller.js, write:
app.controller("AngularCtrl", function ($scope, angularService) {
$scope.divEmpModification = false;
GetAll();
function GetAll() {
var Data = angularService.getEmp();
Data.then(function (emp) {
$scope.employees = emp.data;
}, function () {
alert('Error');
});
}
$scope.edit = function (employee) {
$scope.ID = employee.ID;
$scope.FirstName = employee.FirstName;
$scope.LastName = employee.LastName;
$scope.UserName = employee.UserName;
$scope.Password = employee.Password;
$scope.Operation = "Update";
$scope.divEmpModification = true;
}
$scope.add = function () {
$scope.ID ="";
$scope.FirstName = "";
$scope.LastName = "";
$scope.UserName = "";
$scope.Password = "";
$scope.Operation = "Add";
$scope.divEmpModification = true;
}
$scope.Save = function () {
var Employee = {
FirstName: $scope.FirstName ,
LastName: $scope.LastName ,
UserName: $scope.UserName,
Password: $scope.Password
};
var Operation = $scope.Operation;
if (Operation == "Update") {
Employee.ID = $scope.ID;
var getMSG = angularService.update(Employee);
getMSG.then(function (messagefromController) {
GetAll();
alert(messagefromController.data);
$scope.divEmpModification = false;
}, function () {
alert('Update Error');
});
}
else {
var getMSG = angularService.Add(Employee);
getMSG.then(function (messagefromController) {
GetAll();
alert(messagefromController.data);
$scope.divEmpModification = false;
}, function () {
alert('Insert Error');
});
}
}
$scope.delete = function (employee) {
var getMSG = angularService.Delete(employee.ID);
getMSG.then(function (messagefromController) {
GetAll();
alert(messagefromController.data);
}, function () {
alert('Delete Error');
});
}
});
app.controller("AngularCtrlRole", function ($scope, angularServiceRole) {
GetAllNames();
GetAllRoles();
GetAllEmpRole();
function GetAllEmpRole() {
var Data = angularServiceRole.getEmpRole();
Data.then(function (emp) {
$scope.views = emp.data;
}, function () {
alert('Error');
});
}
function GetAllNames() {
var Data = angularServiceRole.getName();
Data.then(function (emp) {
$scope.items = emp.data;
}, function () {
alert('Error');
});
}
function GetAllRoles() {
var Data = angularServiceRole.getRole();
Data.then(function (role) {
$scope.roleitems = role.data;
}, function () {
alert('Error');
});
}
$scope.SavePermission = function () {
var Permission = {
ID: $scope.selectedItem.ID,
RoleID: $scope.selectedItemRole.ID
};
var getMSG = angularServiceRole.updateRole(Permission);
getMSG.then(function (messagefromController) {
GetAllNames();
GetAllRoles();
GetAllEmpRole();
alert(messagefromController.data);
}, function () {
alert('Save Permission Error');
});
}
});
- Inside Service.js, write:
app.service("angularService", function ($http) {
this.getEmp = function () {
return $http.get("/Employee/GetAllEmployees");
};
this.update = function (employee) {
var response = $http({
method: "post",
url: "/Employee/Update",
data: JSON.stringify(employee),
dataType: "json"
});
return response;
}
this.Delete = function (empID) {
var response = $http({
method: "post",
url: "/Employee/Delete",
params: {
id: empID
}
});
return response;
}
this.Add = function (employee) {
var response = $http({
method: "post",
url: "/Employee/Add",
data: JSON.stringify(employee),
dataType: "json"
});
return response;
}
});
app.service("angularServiceRole", function ($http) {
this.getEmpRole = function () {
return $http.get("/Permission/GetAllEmpRole");
};
this.getName = function () {
return $http.get("/Permission/GetAllEmpNames");
};
this.getRole = function () {
return $http.get("/Permission/GetAllRoles");
};
this.updateRole = function (permission) {
var response = $http({
method: "post",
url: "/Permission/UpdateRole",
data: JSON.stringify(permission),
dataType: "json"
});
return response;
}
});
References
History
- 1/26/2015: First version
- 1/29/2015: Second version: making one Repository for entities
- 02/15/2015: Third version: Repository Pattern with
GenericRepository
and UnitOfWork
+ Graph explanation
Feedback
Feel free to leave any feedback on this article; it is a pleasure to see your comments and vote about this code. If you have any questions, please do not hesitate to ask me here.