Single Page Application Using ASP.NET MVC And jQuery With CRUD methods

Introduction

In this article, I will tell you how to create a single page application using ASP.NET MVC and jQuery. Without using Angular, React, and other third-party JavaScripts, it is difficult to achieve SPA. In this article, I will explain only controller and UI level interaction. I skipped the data access layer. If you want, please download the attachment which includes the overall code of the application.

NoteI used code first approach for CRUD operations. After downloading the project file, restore packages and change the connection string web.config and run the update-database command in Package Manager Console. 

Required Contents

  • ASP.NET MVC
  • JQUERY
  • Sammy.JS (for Routing)

Steps

Create a new MVC project.

ASP.NET
 
ASP.NET 

Add jQuery and Sammy.js reference to your layout page. Add div tag (MainContent) in which we render all the partial views.

  1. <head>  
  2.     <meta charset="utf-8" />  
  3.     <meta name="viewport" content="width=device-width, initial-scale=1.0">  
  4.     <title>@ViewBag.Title - My ASP.NET Application</title>  
  5.     @Styles.Render("~/Content/css")  
  6.     @Scripts.Render("~/bundles/modernizr")  
  7.     <script src="~/Scripts/jquery-1.10.2.js"></script>  
  8.     <script src="~/Scripts/sammy-0.7.4.js"></script>  
  9. </head>  

Create layout-routing.js file in your project which contains your application routing structure which is shown as below.

  1. var mainContent;  
  2. var titleContent;  
  3.   
  4. $(function () {  
  5.     mainContent = $("#MainContent"); /// render partial views.  
  6.     titleContent = $("title"); // render titles.  
  7. });  
  8.   
  9. var routingApp = $.sammy("#MainContent", function () {  
  10.     this.get("#/Student/Index", function (context) {  
  11.         titleContent.html("Student Page");  
  12.         $.get("/Student/Index", function (data) {  
  13.             context.$element().html(data);  
  14.         });  
  15.     });  
  16.   
  17.     this.get("#/Student/Add", function (context) {  
  18.         titleContent.html("Add Student");  
  19.         //$("#BigLoader").modal('show'); // If you want to show loader  
  20.         $.get("/Student/Add", function (data) {  
  21.             //$("#BigLoader").modal('hide');  
  22.             context.$element().html(data);  
  23.         });  
  24.     });  
  25.   
  26.     this.get("#/Student/Edit", function (context) {  
  27.         titleContent.html("Edit Student");  
  28.         $.get("/Student/Edit", {  
  29.             studentID: context.params.id // pass student id  
  30.         }, function (data) {  
  31.             context.$element().html(data);  
  32.         });  
  33.     });  
  34.   
  35.     this.get("#/Home/About", function (context) {  
  36.         titleContent.html("About");  
  37.         $.get("/Home/About", function (data) {  
  38.             context.$element().html(data);  
  39.         });  
  40.     });  
  41.   
  42.     this.get("#/Home/Contact", function (context) {  
  43.         titleContent.html("Contact");  
  44.         $.get("/Home/Contact", function (data) {  
  45.             context.$element().html(data);  
  46.         });  
  47.     });  
  48. });  
  49.   
  50. $(function () {  
  51.     routingApp.run("#/Student/Index"); // default routing page.  
  52. });  
  53.   
  54. function IfLinkNotExist(type, path) {  
  55.     if (!(type != null && path != null))  
  56.         return false;  
  57.   
  58.     var isExist = true;  
  59.   
  60.     if (type.toLowerCase() == "get") {  
  61.         if (routingApp.routes.get != undefined) {  
  62.             $.map(routingApp.routes.get, function (item) {  
  63.                 if (item.path.toString().replace("/#""#").replace(/\\/g, '').replace("$/""").indexOf(path) >= 0) {  
  64.                     isExist = false;  
  65.                 }  
  66.             });  
  67.         }  
  68.     } else if (type.toLowerCase() == "post") {  
  69.         if (routingApp.routes.post != undefined) {  
  70.             $.map(routingApp.routes.post, function (item) {  
  71.                 if (item.path.toString().replace("/#""#").replace(/\\/g, '').replace("$/""").indexOf(path) >= 0) {  
  72.                     isExist = false;  
  73.                 }  
  74.             });  
  75.         }  
  76.     }  
  77.     return isExist;  
  78. }  
IfLinkNotExist() check if url should not repeat, after we will add dynamic url in routing list on page load.

Add layout-routing reference in _layout.cshtml page.

  1. <script src="~/layout-routing.js"></script>  
  2.     @*@Scripts.Render("~/bundles/jquery")*@  
  3.     @Scripts.Render("~/bundles/bootstrap")  
  4.     @RenderSection("scripts", required: false)  

Add a new controller ‘WelcomeController’ which has only one action ‘Index’.

  1. namespace MvcSpaDemo.Controllers  
  2. {  
  3.     public class WelcomeController : Controller  
  4.     {  
  5.         public ActionResult Index()  
  6.         {  
  7.             return View();  
  8.         }  
  9.     }  
  10. }  
Create the View of ‘Index’ action using right-click.

In that View, attach the layout page link. (We need to render the layout page for the first time).

  1. @{  
  2.     ViewBag.Title = "Index";  
  3.     Layout = "~/Views/Shared/_Layout.cshtml";  
  4. }  
  5.   
  6. <h1>Welcome</h1>  
Now, create student Controller which includes student CRUD operation modules. (Add, update, delete, View).

View Students 
  1.  public ActionResult Index()  
  2.  {  
  3.      return PartialView();  
  4.  }  
  5.   
  6.  public ActionResult _Index()  
  7.  {  
  8.      var students = StudentData.GetStudents();  
  9.      return PartialView(students);  
  10.   }  

Add two Views without layout page. One for outer student contents like header, add button, etc. and another for student table.

Index.cshtml 
  1. @{  
  2.     Layout = null;  
  3. }  
  4.   
  5. <h4>Students</h4>  
  6.   
  7. <div class="row">  
  8.     <a href="#/Student/Add">Add Student</a>  
  9. </div>  
  10.   
  11. <div class="row">  
  12.     <div id="StudentDiv">  
  13.     </div>  
  14. </div>  
  15.   
  16. <script>  
  17.     $(function () {  
  18.         GetStudents();  
  19.     });  
  20.   
  21.     function GetStudents() {  
  22.         $.get("/Student/_Index/", function (data) {  
  23.             $("#StudentDiv").html(data);  
  24.         });  
  25.     }  
  26.   
  27.     function DeleteStudent(studentID) {  
  28.         if (confirm("Delete student?")) {  
  29.             $.get("/Student/Delete/", { studentID: studentID }, function (data) {  
  30.                 GetStudents();  
  31.             });  
  32.         }  
  33.     }  
  34. </script>  
_Index.cshtml
  1. @model IEnumerable<MvcSpaDemo.Entities.Student>  
  2. @{  
  3.     Layout = null;  
  4. }  
  5.   
  6. <table class="table table-striped table-bordered">  
  7.     <thead>  
  8.         <tr>  
  9.             <th>ID</th>  
  10.             <th>Name</th>  
  11.             <th>Email</th>  
  12.             <th>Class</th>  
  13.             <th>Action</th>  
  14.         </tr>  
  15.     </thead>  
  16.     <tbody>  
  17.         @foreach (var item in Model)  
  18.         {  
  19.             <tr>  
  20.                 <td>@item.StudentID</td>  
  21.                 <td>@item.FirstName @item.LastName</td>  
  22.                 <td>@item.Email</td>  
  23.                 <td>@item.Class</td>  
  24.                 <td>  
  25.                     <a href="#/Student/[email protected]">Edit</a>  
  26.                     <a href="javascript::;" onclick="DeleteStudent('@item.StudentID')">Delete</a>  
  27.                 </td>  
  28.             </tr>  
  29.         }  
  30.     </tbody>  
  31. </table>  
Change the default controller and action in RouteConfig.cs.
  1. public class RouteConfig  
  2.     {  
  3.         public static void RegisterRoutes(RouteCollection routes)  
  4.         {  
  5.             routes.IgnoreRoute("{resource}.axd/{*pathInfo}");  
  6.   
  7.             routes.MapRoute(  
  8.                 name: "Default",  
  9.                 url: "{controller}/{action}/{id}",  
  10.                 defaults: new { controller = "Welcome", action = "Index", id = UrlParameter.Optional }  
  11.             );  
  12.         }  
  13.     }  

Run the application using F5. Do the same routing for About and Contact page also.

ASP.NET
Add Student asd 

Now, add "Create Student Actions" in Controller.

  1. public ActionResult Add()  
  2.         {  
  3.             var student = new Student();  
  4.             ViewBag.Status = "Add";  
  5.             return PartialView(student);  
  6.         }  
  7.   
  8.         [HttpPost]  
  9.         public ActionResult Create(Student student)  
  10.         {  
  11.             StudentData.AddStudent(student);  
  12.             return Json(true, JsonRequestBehavior.AllowGet);  
  13.         }  

We will add the add page with dynamic routing script for create or update.

  1. @model MvcSpaDemo.Entities.Student  
  2. @{  
  3.     ViewBag.Title = "Add";  
  4.     Layout = null;  
  5.   
  6.     // We use same page for add and edit.  
  7.     var urlPostString = "/Student/Create";  
  8.     if (ViewBag.Status == "Edit")  
  9.     {  
  10.         urlPostString = "/Student/Update";  
  11.     }  
  12. }  
  13.   
  14. <h2>@ViewBag.Status Student</h2>  
  15.   
  16. <form id="frmStudent" name="frmStudent" method="post" action="#@urlPostString">  
  17.     @Html.HiddenFor(x => x.StudentID)  
  18.     <div class="row">  
  19.         <div class="form-group">  
  20.             <label for="Variables">First Name</label>  
  21.             @Html.TextBoxFor(x => x.FirstName, new { @class = "form-control square" })  
  22.         </div>  
  23.         <div class="form-group">  
  24.             <label for="Variables">Last Name</label>  
  25.             @Html.TextBoxFor(x => x.LastName, new { @class = "form-control square" })  
  26.         </div>  
  27.         <div class="form-group">  
  28.             <label for="Variables">Email</label>  
  29.             @Html.TextBoxFor(x => x.Email, new { @class = "form-control square" })  
  30.         </div>  
  31.         <div class="form-group">  
  32.             <label for="Variables">Class</label>  
  33.             @Html.TextBoxFor(x => x.Class, new { @class = "form-control square" })  
  34.         </div>  
  35.         <div class="form-group">  
  36.             <input type="submit" class="btn btn-primary" value="Submit" />  
  37.         </div>  
  38.     </div>  
  39. </form>  
  40.   
  41. <script>  
  42.     $("#frmStudent").on("submit", function (e) {  
  43.         debugger;  
  44.         //if ($("#frmStudent").valid()) {  
  45.         routingApp.runRoute('post''#@urlPostString');  
  46.         e.preventDefault();  
  47.         e.stopPropagation();  
  48.         //}  
  49.     });  
  50.   
  51.     // add dynamic create or update link  
  52.   
  53.     debugger;  
  54.     if (IfLinkNotExist("POST""#@urlPostString")) {  
  55.         routingApp.post("#@urlPostString", function (context) {  
  56.             //$("#BigLoader").modal('show');  
  57.             var formData = new FormData($('#frmStudent')[0]);  
  58.             $.ajax({  
  59.                 url: '@urlPostString',  
  60.                 data: formData,  
  61.                 type: "POST",  
  62.                 contentType: false,  
  63.                 processData: false,  
  64.                 success: function (data) {  
  65.                     //$("#BigLoader").modal('hide');  
  66.                     if (data) {  
  67.                         if ('@ViewBag.Status' == 'Add')  
  68.                             alert("Student successfully added");  
  69.                         else if ('@ViewBag.Status' == 'Edit')  
  70.                             alert("Student successfully updated");  
  71.                         window.location.href = "#/Student/Index";  
  72.                     }  
  73.                     else {  
  74.                         alert('Something went wrong');  
  75.                     }  
  76.                 },  
  77.                 error: function () {  
  78.                     alert('Something went wrong');  
  79.                 }  
  80.             });  
  81.         });  
  82.     }  
  83.   
  84. </script>  

Now, run the application.

ASP.NET 
ASP.NET 
ASP.NET 
Edit Student

Now, move on to Edit Module. Add Edit actions in Controller.

  1. public ActionResult Edit(int studentID) // studentID nothing but parameter name which we pass in layout-routing.  
  2.         {  
  3.             var student = StudentData.GetStudentById(studentID);  
  4.             ViewBag.Status = "Edit";  
  5.             return PartialView("Add", student);  
  6.         }  
  7.   
  8.         [HttpPost]  
  9.         public ActionResult Update(Student student)  
  10.         {  
  11.             StudentData.UpdateStudent(student);  
  12.             return Json(true, JsonRequestBehavior.AllowGet);  
  13.         }  

We used the same partial view for add and edit, so there  is no need to create a new View for edit. 

After adding the action methods, just run the application.

ASP.NET 
ASP.NET 
ASP.NET 
 
Delete Student 

Now, we will implement the Delete operation.

We have already written Delete button's code in Student Index.cshtml.

  1. function GetStudents() {  
  2.         $.get("/Student/_Index/", function (data) {  
  3.             $("#StudentDiv").html(data);  
  4.         });  
  5.     }  
  6.   
  7.     function DeleteStudent(studentID) {  
  8.         if (confirm("Delete student?")) {  
  9.             $.get("/Student/Delete/", { studentID: studentID }, function (data) {  
  10.                 GetStudents();  
  11.             });  
  12.         }  
  13.     }  

Run the application.

ASP.NET 
 
ASP.NET 

I hope you have enjoyed this article. Your valuable feedback, questions, or comments about this article are always welcome.