I tried to learn Single Page Application (SPA) from online and they are good ones for the starters with mainly CRUD operations on only one database table. In real time there will be lot of tables you have to retrieve data from and insert into and I couldn't find any such article. So, I have decided to create on my own and thanks to my project manager who has asked me to show demo of SPA using AngularJS framework.
This is two part demo, in first part I will be showing, how to insert a record into database and get all the records from database, and display. Part 2 I will show delete and update operations.
Article Overview: CRUD operations on multiple database tables in SPA.
Introduction
Now a day's user wants to faster web application with Rich UI and able to access app from multiple gadgets (like Tablets, Mobiles etc.). With help of HTML5, JQuery or JavaScript frameworks and AJAX it is possible to develop SPA.
What is SPA in Reality
In simple words it is nothing but a web application with one parent page and child pages (partial views) are loaded based on demand. In SPA all the required resources are loaded with initial request and rest of the resources are loaded based on demand.
SPA vs Traditional Web App’s
Traditional Web App’s
As you can see in below image the traditional app’s whenever user request, it goes back to server and render’s new page with page reload.
Source: https://msdn.microsoft.com/en-us/magazine/dn463786.aspx?f=255&MSPPError=-2147217396
SP App
In SPA when user request page, it will dynamically load and calls to server will be done through Ajax and data is retrieved by JSON. In SPA data rendering and routing is done on client side, which makes very responsive just like desktop apps.
Source: http://www.slideshare.net/hsplmkting/benefits-of-developing-single-page-web-applications-using-angular-js
Advantages of SPA
- No page flicker. Native application feel.
- Client side routing and data rendering on client side.
- Data is from server is in JSON format.
Disadvantages of SPA
- User must enable JavaScript.
- Security.
Creating project in visual studio
In this demo I have used VS 2012, .NET framework 4.5, AngularJS, Bootstrap and .mdf file for database operation. I have provided all necessary screen shot and source code to download.
Open Visual Studio 2012 and create project with MVC4 template, I have named the project as SingePageAngularJS but you can name it anything as you like.
Adding JQuery and AngularJS
Figure below shows I installed Bootstrap, AngularJS and Angular routing from NuGet packages. And added new folder in scripts folder named it as App and added new JavaScript file called myApp. I will be adding angularJS code in myApp later on.
Now add these installed files to your MVC project. Go To -> App_Start -> BundleConfig. Add below lines like I have showed in red rectangle in the below figure.
Now you have to make sure these files are added in your Master page (_Layout.cshtml) which is located in Views -> Shared folder of the application. I always put my JQuery or required files inside the head tag of my project, to make it accessible to you entire application as shown in the below image.
So, now we have added JQuery, angular, angular routing, bootstrap css and bootstrap.js files in our layout page and we need to make sure they are rendered on our browser properly without any errors. To double check run the application in chrome browser and press F12 to open browser tools. Check all the files are loaded by clicking on Sources tab and check there are no errors on console tab. Below figure confirms all files are loaded properly with no errors in console tab.
Setting up Database
I will add .mdf file for database operations and named it has AngularDB.mdf. In .mdf file I have created two tables (EmpDetails
and EmpAddress
) and scripts are provided below. EmpDetails
table contains employee information and EmpAddress
contains employee multiple address. Between these two tables we have foreign key relation with EmpId
because whenever you select employee I wanted to display all the addresses of the particular employee.
EmpDetails Table
CREATE TABLE [dbo].[EmpDetails] (
[EmpID] INT IDENTITY (1, 1) NOT NULL,
[EmpName] VARCHAR (50) NULL,
[EmpPhone] VARCHAR (50) NULL,
PRIMARY KEY CLUSTERED ([EmpID] ASC)
);
EmpAddress Table
CREATE TABLE [dbo].[EmpAddress] (
[EmpAddressId] INT IDENTITY (1, 1) NOT NULL,
[Address1] VARCHAR (500) NULL,
[Address2] VARCHAR (500) NULL,
[Address3] VARCHAR (500) NULL,
[EmpID] INT NULL,
PRIMARY KEY CLUSTERED ([EmpAddressId] ASC),
FOREIGN KEY ([EmpID]) REFERENCES [dbo].[EmpDetails] ([EmpID])
);
Note: In this application my concentration is not only on CRUD operations but also to show how to display data from multiple tables to UI and pass data to multiple tables.
MVVM Pattern
For this demo I will be using MVVM pattern for both in angularJS script and visual studio. The image below shows I have created folder called ViewModels (it has references of multiple models) and add two models in the models folder, and I name them as EmpDetailsModel
and EmpAddressModel
.
Angularjs Code
In previous section we have done with database related stuff. Now we jump into main topic of this demo.
In Adding Jquery and Angularjs: we have created myApp.js file and left it empty, just copy and paste below code in it.
angular.module('App', ['AngularDemo.EmpAddController',
'AngularDemo.AddressController',
'AngularDemo.DeleteController'
])
.config(['$routeProvider', '$locationProvider', function ($routeProvider, $locationProvider) {
$routeProvider.when('/', {
templateUrl: '/Home/AddEmployee',
controller: 'EmpAddCtrl',
});
$routeProvider.when('/Edit', {
templateUrl: '/Home/EditEmployee',
controller: 'EditCtrl'
});
$routeProvider.when('/Delete', {
templateUrl: '/Home/DeleteEmployee',
controller: 'DeleteCtrl'
});
$routeProvider.otherwise({
redirectTo: '/'
});
$locationProvider.html5Mode(false).hashPrefix('!');
}]);
angular.module('AngularDemo.EmpAddController', ['ngRoute'])
.controller('EmpAddCtrl', function ($scope, $http) {
$scope.EmpAddressList = {};
$http.get('/Home/ShowEmpList').success(function (data) {
$scope.EmpAddressList = data;
});
$scope.EmpDetailsModel =
{
EmpID: '',
EmpName: '',
EmpPhone: ''
};
$scope.EmpAddressModel =
{
Address1: '',
Address2: '',
Address3: ''
};
$scope.EmployeeViewModel = {
empDetailModel: $scope.EmpDetailsModel,
empAddressModel: $scope.EmpAddressModel
};
$scope.AddEmployee = function () {
$.ajax({
url: '/Home/AddEmpDetails',
type: 'POST',
dataType: 'json',
contentType: 'application/json',
traditional: true,
data: JSON.stringify({ EmployeeViewModelClient: $scope.EmployeeViewModel }),
success: function (data) {
$scope.EmpAddressList.push(data[0]);
$scope.$apply();
alert("Record is been added");
}
});
};
});
angular.module('AngularDemo.AddressController', ['ngRoute'])
.controller('EditCtrl', function ($scope, $http) {
$scope.Message = "Edit in Part 2 is coming soon";
});
angular.module('AngularDemo.DeleteController', ['ngRoute'])
.controller('DeleteCtrl', function ($scope, $http) {
$scope.Message = "Delete in Part 2 is coming soon";
});
Angular Code Explaination:
angular.module('App', ['AngularDemo.EmpAddController',
'AngularDemo.AddressController',
'AngularDemo.DeleteController'
])
Note: The angular.module is a global place for creating, registering and retrieving Angular modules. All modules that should be available to an application must be registered using this mechanism. (https://docs.angularjs.org/api/ng/function/angular.module).
1st parameter is the name of your angular app, for our demo it is named as ‘App’.
2nd parameter is array of dependencies, we have only three dependencies currently that is 'AngularDemo.EmpAddController', 'AngularDemo.AddressController' and 'AngularDemo.DeleteController'.
ng-app directive: Once you name your angularJS you have to use in your html page depending up on your requirements. For this demo I have used ng-app in _Layout page in html tag like shown below.
Client-side Routing
So, we have defined the name of our angularJS app and mentioned array of dependencies. Now, let’s create routing at the client side. For this I have created three routes as below with templateUrl
and controller.
.config(['$routeProvider', '$locationProvider', function ($routeProvider, $locationProvider) {
$routeProvider.when('/', {
templateUrl: '/Home/AddEmployee',
controller: 'EmpAddCtrl',
});
$routeProvider.when('/Edit', {
templateUrl: '/Home/EditEmployee',
controller: 'EditCtrl'
});
$routeProvider.when('/Delete', {
templateUrl: '/Home/DeleteEmployee',
controller: 'DeleteCtrl'
});
$routeProvider.otherwise({
redirectTo: '/'
});
$locationProvider.html5Mode(false).hashPrefix('!');
}]);
Whenever, angularJS finds the url which was mentioned in routing, it goes corresponding angularJS controller, where we have to create models and Ajax calls to server. Below is the controller code.
angular.module('AngularDemo.EmpAddController', ['ngRoute'])
.controller('EmpAddCtrl', function ($scope, $http) {
$scope.EmpDetailsModel =
{
EmpID: '',
EmpName: '',
EmpPhone: ''
};
$scope.EmpAddressModel =
{
Address1: '',
Address2: '',
Address3: ''
};
$scope.EmployeeViewModel = {
empDetailModel: $scope.EmpDetailsModel,
empAddressModel: $scope.EmpAddressModel
};
$scope.EmpAddressList = {};
$http.get('/Home/ShowEmpList').success(function (data) {
$scope.EmpAddressList = data;
});
$scope.AddEmployee = function () {
$.ajax({
url: '/Home/AddEmpDetails',
type: 'POST',
dataType: 'json',
contentType: 'application/json',
traditional: true,
data: JSON.stringify({ EmployeeViewModelClient: $scope.EmployeeViewModel }),
success: function (data) {
$scope.EmpAddressList.push(data[0]);
$scope.$apply();
alert("Record is been added");
}
});
};
});
$scope
is responsible for setting model properties and functions/behaviour of particular controller.
In EmpAddCtrl
controller we have two models(EmpDetailsModel
, EmpAddressModel
) and viewmodel (EmployeeViewModel
) where we will be passing this viewmodel to server to save data to database, using $scope.AddEmployee
function and $http.get
will get all the list of records on pageloads.
Views: Now we have done with angularJS we have to use this in html pages. So, this is what my _layout page looks like. _Layout.html
<!DOCTYPE html>
<html lang="en" ng-app="App">
<head>
<meta charset="utf-8" />
<title>@ViewBag.Title - My ASP.NET MVC Application</title>
<link href="~/favicon.ico" rel="shortcut icon" type="image/x-icon" />
<meta name="viewport" content="width=device-width" />
@Styles.Render("~/Content/css")
@Scripts.Render("~/bundles/jquery")
@Scripts.Render("~/bundles/bootstrap")
@Scripts.Render("~/bundles/angular")
@Scripts.Render("~/bundles/CustomAngular")
@Scripts.Render("~/bundles/modernizr")
</head>
<body>
<header>
<nav class="navbar navbar-default navbar-static-top">
<div class="container">
<div class="navbar-header">
<button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#navbar" aria-expanded="false" aria-controls="navbar">
<span class="sr-only">Toggle navigation</span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
</div>
<div id="navbar" class="navbar-collapse collapse">
<ul class="nav navbar-nav">
<li class="active"><a href="#!/">Add</a></li>
<li><a href="#!/Address">Edit/Update</a></li>
<li><a href="#!/Delete">Delete</a></li>
</ul>
</div>
<!--
</div>
</nav>
</header>
<div id="body">
@RenderSection("featured", required: false)
<section class="content-wrapper main-content clear-fix">
@RenderBody()
</section>
</div>
@RenderSection("scripts", required: false)
</body>
</html>
Index.html page
Just copy below code and paste it in index html page. ng-view is place holder where the partial views are dynamically loaded.
<div ng-view="" ></div>
AddEmployee.html partial page
Copy below code and paste it in your AddEmployee
partial view. In this view user can add new employee and address information and it also display list of employees in grid format.
The ng-model directive binds the value of HTML controls (input, select, textarea) to application data.
<div style="width: 50%; margin: 50px auto;">
<table>
<tr>
<td>
<strong>Employee Name:</strong>
</td>
<td>
<input type="text" class="form-control" ng-model="EmpDetailsModel.EmpName" placeholder="Employee Name" />
</td>
</tr>
<tr>
<td>
<strong>Employee Phone:</strong>
</td>
<td>
<input type="text" class="form-control" ng-model="EmpDetailsModel.EmpPhone" placeholder="Employee Phone" />
</td>
</tr>
<tr>
<td>
<strong>Address 1:</strong>
</td>
<td>
<input type="text" class="form-control" ng-model="EmpAddressModel.Address1" placeholder="Address 1" />
</td>
</tr>
<tr>
<td>
<strong>Address 2:</strong>
</td>
<td>
<input type="text" class="form-control" ng-model="EmpAddressModel.Address2" placeholder="Address 2" />
</td>
</tr>
<tr>
<td>
<strong>Address 3:</strong>
</td>
<td>
<input type="text" class="form-control" ng-model="EmpAddressModel.Address3" placeholder="Address 3" />
</td>
</tr>
<br />
<tr>
<td>
</td>
<td>
<button type="button" ng-click="AddEmployee();" class="btn btn-primary">Save</button>
</td>
</tr>
</table>
</div>
<hr style="color: black" />
<div style="width: 50%; margin: 50px auto;">
<div class="panel panel-default">
<!--
<div class="panel-heading"><b>Employee Details </b></div>
<div class="table-responsive">
<table id="EmployeeTable" class="table table-striped table-bordered table-hover table-condensed">
<thead>
<tr>
<th>Employee Name</th>
<th>Employee Phone</th>
<th>Employee Address1</th>
<th>Employee Address2</th>
<th>Employee Address3</th>
</tr>
</thead>
<tbody>
<tr data-ng-repeat="Emp in EmpAddressList">
<td>{{Emp.empDetailModel.EmpName}}</td>
<td>{{Emp.empDetailModel.EmpPhone}}</td>
<td>{{Emp.empAddressModel.Address1}}</td>
<td>{{Emp.empAddressModel.Address2}}</td>
<td>{{Emp.empAddressModel.Address3}}</td>
</tr>
@*<tr ng-if="states.NewRow">*@
<tr ng-if="EmpAddressList.length == 0">
<td class="text-center" colspan="4">There are no Employee details to display
</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
Output screens
Now we have done with all our coding, just run by pressing F5 you can see below screenshot. Initially we don't have any data so, therefore it display's There are no Employee details to display.
Save Button
Enter data of employee and address, when User clicks on save button, it triggers AddEmployee function in angularjs which calls corresponding controller action. Below image shows that we have entered information.
Once user clicks on save, the view model is passed from angularjs to mvc controller action. I have two screen shots below to show data being passed to models (empDetailsModel
and empAddressModel
).
After saving into database you will get confirmation saying record is been added.
Database
The data we have entered on UI is been saved to database.
Conclusion
Single page application will give you better performance when compared to traditional web apps. But, never compromise when it comes to security and you should think about security and take enough measures about it before developing single page applications.