I tried to learn about SPAs from online articles and there are some good ones for beginners with mainly CRUD operations for only one database table. In the real world there are many tables you need 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 I thank my project manager for asking me to show a sample using the AngularJs framework.
This is a two-part demo, in the first part I will be showing how to insert a record into a database and get all the records from the database and display them. Part 2 will show the delete and update operations.
Introduction
These days users want faster web applications with a Rich UI and to be able to access apps from multiple gadgets (like Tablets, Mobiles and so on). HTML5 and jQuery or JavaScript can be used to build Single Page Applications (SPAs).
SPAs
SPAs are web applications that fit in a single page and that provide a more fluid user experience, just like the desktop with a rich UI. SPAs are web apps that load a single HTML page and dynamically update the page as the user interacts with the app.
SPA vs Traditional Web Apps
Traditional Web Apps: You can see in the following image the traditional apps. Whenever the user makes a request, it goes back to the server and renders a new page with a page reload.
Source: https://msdn.microsoft.com/
SP App
In a SPA when the user requests a page it will load dynamically, calls to the server will be done using Ajax and data is retrieved by JSON. In the SPA the data rendering and routing is done on the client side and that makes it responsive just like desktop apps.
The following are advantages of SPAs:
- No page flicker. Native application feel.
- Client-side routing and data rendering on the client side.
- Data from the server is in JSON format.
The following are disadvantages of SPAs:
- User JavaScript must be enabled.
- Security.
Creating a project in Visual Studio
In this demo I have used Visual Studio 2012, .NET Framework 4.5, AngularJs, Bootstrap and .mdf file for the database operations. I have provided all the necessary screenshots and source code to download.
Open Visual Studio 2012 and create a project with MVC4 template, I have named the project SingePageAngularJS, but you can name it anything you like.
Adding jQuery and AngularJs
The following figure shows I installed Bootstrap, AngularJs and Angular routing from NuGet packages. And added a new folder to the scripts folder named App and added a new JavaScript file called myApp. I will be adding AngularJs code in myApp later on.
Now to add these installed files to the MVC project. Go To App_Start, then BundleConfig. Add the following lines as I have shown in the Red rectangle in the following:
Now you need to ensure these files are added to your Master page (_Layout.cshtml) located in the Views -> Shared folder of the application. I always put my jQuery or required files inside the head tag of the project, to make it accessible to your entire application as in the following image:
Now we have added jQuery, Angular, Angular routing, Bootstrap CSS and the bootstrap.js files to our layout page and we need to be sure they are rendered on our browser properly without any errors. To double-check, run the application in the Chrome browser and press F12 to open the browser tools. Check that all the files are loaded by clicking on the Sources tab and check that there are no errors in the console tab. The following confirms that all the files are loaded properly with no errors in the console tab.
Setting up Database
I will add a .mdf file for the database operations and name it AngularDB.mdf. In the .mdf file I created the two tables EmpDetails and EmpAddress and the scripts are provided below. The EmpDetails table contains employee information and EmpAddress contains the employee's multiple address. Between these two tables we have a foreign key relation with EmpId because whenever you select an employee I wanted to display all the addresses of the specific 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 the UI and pass data to multiple tables.
MVVM Pattern
For this demo I will be using the
MVVM pattern for both the AngularJs script and Visual Studio. The following image shows I have created a folder called
ViewModels (it has references of multiple models) and added two models in the models folder. I named them
EmpDetailsModel and
EmpAddressModel.
AngularJs Code
In the previous section we did some database related stuff. Now we jump into the main topic of this demo.
In
Adding jQuery and AngularJs: We have created the myApp.js file and left it empty, now just copy and paste the following code into 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 Explanation
- 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.
The
first parameter is the name of your Angular app, for our demo it is named "App".
The
second parameter is an array of dependencies, we have only two dependencies, currently they are:
'
AngularDemo.EmpAddController', 'AngularDemo.AddressController' and 'AngularDemo.DeleteController'.
ng-app directive
Once you name your AngularJs you need to use it in your HTML page depending upon your requirements. For this demo I have used ng-app in the _Layout page in the HTML tag as in the following code snippet:
Client-side Routing
So, we have defined the name of our AngularJs app and specified an array of dependencies. Now, let’s create the routing at the client side. For this I have created three routes as in the following 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 that was specified in routing, it goes to the corresponding AngularJs controller, where we need to create models and Ajax calls to the server. The following 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 the model properties and the functions/behaviour of the specific controller.
In the EmpAddCtrl controller we have two models (EmpDetailsModel and EmpAddressModel) and viewmodel (EmployeeViewModel) where we will be passing this viewmodel to the server to save data to the database, using the
$scope.AddEmployee function and
$http.get will get the list of all records on pageloads.
Views
Now we are done with AngularJs. We need 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>
- <!--/.nav-collapse -->
- </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 the following code and paste it into the index HTML page.
ng-view is a place holder where the partial views are dynamically loaded.
AddEmployee.html partial page
Copy the following code and paste it into your AddEmployee partial view. In this view the user can add a new employee and address information and it also displays a list of employees in grid format.
The
ng-model directive binds the value of HTML controls (input, select and textarea) to the 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">
- <!-- Default panel contents -->
- <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 are done with all our coding. Run it by pressing F5, you can see the following screenshot. Initially we don't have any data, therefore it displays
There are no Employee details to display.
Save Button
Enter the data of an employee and their address(es), when the user clicks the Save button, it triggers the AddEmployee function in AngularJs that calls the corresponding controller action. The following image shows that we have entered information.
Once the user clicks on Save, the view model is passed from AngularJs to the MVC controller action. I have the following two screenshots to show the data being passed to the models (empDetailsModel and empAddressModel).
After saving into the database you will get confirmation saying the record has been added.
Database
The data we have entered into the UI has been saved to the database.
Conclusion
A Single Page Application will provide you better performance compared to traditional web apps, but never compromise when it comes to security and you should think about security and implement sufficient measures for it before developing Single Page Applications.