Donate

Bootstrap Modal In ASP.NET MVC With CRUD Operations

Good afternoon fellow developers!

I have been working with Bootstrap Modals before and it's time to demonstrate its significance using a simple ASP.NET MVC CRUD (Create Update Delete) application project using Entity Framework Database First approach. First is to create a basic BookDetails table on your SQL Server instance.
USE [DemoDB]
GO

/****** Object:  Table [dbo].[BookDetails]    Script Date: 11/2/2020 12:26:53 PM ******/
SET ANSI_NULLS ON
GO

SET QUOTED_IDENTIFIER ON
GO

CREATE TABLE [dbo].[BookDetails](
	[BookSerialNo] [int] IDENTITY(1,1) NOT NULL,
	[BookISBN] [nchar](15) NULL,
	[BookTitle] [varchar](120) NULL,
	[BookAuthor] [varchar](60) NULL,
	[BookPublisher] [varchar](50) NULL,
	[BookCategory] [varchar](20) NULL,
PRIMARY KEY CLUSTERED 
(
	[BookSerialNo] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
GO
Next is to create an ASP.NET MVC project and add references to jQuery and Bootstrap Scripts + CSS. Then establish a connection between the created table and your project by adding an Entity Framework. In my situation, I named it as BooksEntities. Once a connection has been created, you already have a model class defined by the Entity Framework called BookDetails. The connection string generated by the Entity Framework is shown below.
<connectionStrings>
    <add name="BooksEntities" connectionString="metadata=res://*/Models.BooksEntities.csdl|res://*/Models.BooksEntities.ssdl|res://*/Models.BooksEntities.msl;provider=System.Data.SqlClient;provider connection string=&quot;data source=.;initial catalog=DemoDB;Integrated Security=True;MultipleActiveResultSets=True;App=EntityFramework&quot;" providerName="System.Data.EntityClient" />
  </connectionStrings>
In your Layout.cshtml page, make sure to reference the jQuery and Bootstrap scripts since these will be used by the modals.
@Scripts.Render("~/bundles/jquery")
@Scripts.Render("~/bundles/bootstrap")    
<script type="text/javascript" src="~/Scripts/BookDetail.js"></script>
In your models folder, add a class for paging records. This will be used by the view later.
using System;
using System.Collections.Generic;

namespace BootstrapDemo.Models
{
    public class PagedList<T>
    {
        public List<T> Content { get; set; }
        public Int32 CurrentPage { get; set; }
        public Int32 PageSize { get; set; }
        public int TotalRecords { get; set; }
        public int TotalPages
        {
            get { return (int)Math.Ceiling((decimal)TotalRecords / PageSize); }
        }
    }
}
Create a view model class inside the Models folder that contains a property for the BookDetail class that is used by the controller and partial views for model binding.
public class BooksViewModel
{
   public BookDetail BookDetail { get; set; }
}
Create a view for the Index that has a table that shows the records from the database with the ability to create, update and delete those records. The view uses WebGrid Class from System.Web.Helpers namespace to display those information in a tabular manner. You may use other types of front-end tables to show data since the emphasis of this tutorial is how to use the Bootstrap Modal. Below is the main view that contains the code of the table and modal placeholders for Create, Update, Delete and Show Details.
@{
    ViewBag.Title = "Home Page";
}

@using BootstrapDemo.Models
@model BootstrapDemo.Models.PagedList<BookDetail>
<script type="text/javascript" src="~/Scripts/jquery-1.10.2.js"></script>

<br />
<div>
    <a class="btn btn-success" data-modal="" id="btnCreate" onclick="OpenAddModal()">
        <span class="glyphicon glyphicon-plus"></span>
    </a>
</div>
<div style="margin-top:17px;">

    @{
        var grid = new WebGrid(
                    canPage: true,
                    rowsPerPage: Model.PageSize,
                    canSort: false,
                    ajaxUpdateContainerId: "grid");
        
        grid.Bind(Model.Content, rowCount: Model.TotalRecords, autoSortAndPage: false);
        grid.Pager(mode: WebGridPagerModes.All); 

        @grid.Table(htmlAttributes: new { id = "grid" },   
            fillEmptyRows: false,
            tableStyle: "table table-stripped table-hover",
            columns: grid.Columns(
                       grid.Column("BookSerialNo", "Serial No."),
                       grid.Column("BookISBN", "ISBN"),
                       grid.Column("BookTitle", "Title"),
                       grid.Column("BookAuthor", "Author"),
                       grid.Column("BookPublisher", "Publisher"),
                       grid.Column("BookCategory", "Category"),
                       grid.Column(header: "Action", canSort: false, style: "action",
                        format: @<text>
                                @Html.Raw("<a id='ViewEdit' data-modal='' onclick='OpenEditModal(" + item.BookSerialNo + ")'  title='Edit'> <span class='glyphicon glyphicon-edit'> </span> </a>")
                                @Html.Raw("<a id='ViewDelete' data-modal='' onclick='OpenDeleteModal(" + item.BookSerialNo + ")'  title='Delete'> <span class='glyphicon glyphicon-trash'> </span> </a>")
                                @Html.Raw("<a id='ViewDetail' data-modal=''  onclick='OpenDetailsModal(" + item.BookSerialNo + ")'  title='Details'> <span class='glyphicon glyphicon-search'> </span> </a>")
                            </text>)
                                                      ));        
        <div style="text-align:right;margin-top:-30px;">
            @grid.PagerList(mode: WebGridPagerModes.All, paginationStyle: "pagination pagination-sm pagination-right")
        </div>
    }

</div>

<!-- modal placeholders-->
<div id="modal-edit" class="modal fade">
    <div class="modal-dialog modal-sm">
        <div class="modal-content">
            <div id='modal-edit-content'></div>
        </div>
    </div>
</div>
<div id="modal-add" class=" modal fade in">
    <div class="modal-dialog modal-sm">
        <div class="modal-content">
            <div id='modal-add-content'></div>
        </div>
    </div>
</div>
<div id="modal-delete" class=" modal fade in">
    <div class="modal-dialog modal-sm">
        <div class="modal-content">
            <div id='modal-delete-content'></div>
        </div>
    </div>
</div>
<div id="modal-details" class="modal fade in">
    <div class="modal-dialog modal-sm">
        <div class="modal-content">
            <div id='modal-details-content'></div>
        </div>
    </div>
</div>
Create an external JavaScript file called BookDetail.js that contains the functions to open the Bootstrap modals.
function OpenEditModal(id) {
    var data = { serialNumber: id };
    $.ajax(
        {
            type: 'GET',
            url: '/Home/Edit',
            contentType: 'application/json; charset=utf=8',
            data: data,
            success: function (result) {
                $('#modal-edit-content').html(result);
                $('#modal-edit').modal('show');
            },
            error: function (er) {
                alert(er);
            }
        });
}

function OpenDeleteModal(id) {
    var data = { serialNumber: id };
    $.ajax(
        {
            type: 'GET',
            url: '/Home/Delete',
            contentType: 'application/json; charset=utf=8',
            data: data,
            success: function (result) {
                $('#modal-delete-content').html(result);
                $('#modal-delete').modal('show');
            },
            error: function (er) {
                alert(er);
            }
        });
}

function OpenDetailsModal(id) {
    var data = { serialNumber: id };
    $.ajax(
        {
            type: 'GET',
            url: '/Home/Details',
            contentType: 'application/json; charset=utf=8',
            data: data,
            success: function (result) {
                $('#modal-details-content').html(result);
                $('#modal-details').modal('show');
            },
            error: function (er) {
                alert(er);
            }
        });
}

function OpenAddModal() {
    $.ajax(
        {
            type: 'GET',
            url: '/Home/Create',
            contentType: 'application/json; charset=utf=8',
            success: function (result) {
                $('#modal-add-content').html(result);
                $('#modal-add').modal('show');
            },
            error: function (er) {
                alert(er);
            }
        });
}
In your controller class, add the codes to create, update, delete and show information from the database.
using BootstrapDemo.Models;
using System.Data.Entity;
using System.Linq;
using System.Web.Mvc;

namespace BootstrapDemo.Controllers
{
    public class HomeController : Controller
    {
        private BooksEntities db;
        
        public ActionResult Index(int page = 1, int pageSize = 10)
        {            
            var model = new PagedList<BookDetail>();
           
            using (db = new BooksEntities())
            {
                model.Content = db.BookDetails
                                .OrderBy(p => p.BookSerialNo)
                                .Skip((page - 1) * pageSize)
                                .Take(pageSize)
                                .ToList();

                model.TotalRecords = db.BookDetails.ToList().Count;
            }            

            model.CurrentPage = page;
            model.PageSize = pageSize;

            return View(model);
        }

        [HttpGet]
        public PartialViewResult Create()
        {
            return PartialView("AddBookDetails");
        }

        [HttpPost]
        public ActionResult Create(BooksViewModel model)
        {
            using (db = new BooksEntities())
            {
                if (model != null)
                {
                    db.BookDetails.Add(model.BookDetail);
                    db.SaveChanges();
                }
            }

            return RedirectToAction("Index");
        }

        [HttpGet]
        public PartialViewResult Edit(int? serialNumber)
        {
            var model = new BooksViewModel();

            using (db = new BooksEntities())
            {
                model.BookDetail = (from item in db.BookDetails
                                    where item.BookSerialNo == serialNumber
                                    select item).FirstOrDefault();
            }         

            return PartialView("EditBookDetails", model);
        }

        [HttpPost]
        public ActionResult Edit(BooksViewModel model)
        {
            using (db = new BooksEntities())
            {
                var record = db.BookDetails.Where(t => t.BookSerialNo == model.BookDetail.BookSerialNo);

                if (record != null)
                {
                    db.Entry(model.BookDetail).State = EntityState.Modified;
                    db.SaveChanges();
                }
            }

            return RedirectToAction("Index");
        }

        [HttpGet]
        public PartialViewResult Delete(int? serialNumber)
        {
            var model = new BooksViewModel();

            using (db = new BooksEntities())
            {
                model.BookDetail = (from item in db.BookDetails
                                    where item.BookSerialNo == serialNumber
                                    select item).FirstOrDefault();
            }

            return PartialView("DeleteBookDetails", model);
        }

        [HttpPost]
        public ActionResult Delete(BooksViewModel model)
        {
            using (db = new BooksEntities())
            {
                if (model != null)
                {
                    var obj = db.BookDetails.Find(model.BookDetail.BookSerialNo);
                    db.BookDetails.Remove(obj);
                    db.SaveChanges();
                }
            }

            return RedirectToAction("Index");
        }

        [HttpGet]
        public PartialViewResult Details(int? serialNumber)
        {
            var model = new BooksViewModel();

            using (db = new BooksEntities())
            {
                model.BookDetail = (from item in db.BookDetails
                                    where item.BookSerialNo == serialNumber
                                    select item).FirstOrDefault();
            }

            return PartialView("BookDetails", model);
        }

        public ActionResult About()
        {
            ViewBag.Message = "Your application description page.";

            return View();
        }

        public ActionResult Contact()
        {
            ViewBag.Message = "Your contact page.";

            return View();
        }
    }
}
Finally, add partial views for the CRUD operations since these views are the content of the Bootstrap Modals.
Create Modal - AddBookDetails.cshtml.
@model BootstrapDemo.Models.BooksViewModel
<link rel="stylesheet" href="~/Content/bootstrap.min.css" />
<script type="text/javascript" src="~/Scripts/bootstrap.min.js"></script>

@using (Html.BeginForm("Create", "Home", FormMethod.Post))
{
    @Html.AntiForgeryToken()

    <h3 class="modal-title">Add New Item</h3>
    <hr />
    @Html.ValidationSummary(true)
    <div class="modal-body">
        @Html.LabelFor(model => model.BookDetail.BookISBN, new { @class = "control-label" })
        @Html.TextBoxFor(model => model.BookDetail.BookISBN, new { @class = "form-control" })
        @Html.ValidationMessageFor(model => model.BookDetail.BookISBN)

        @Html.LabelFor(model => model.BookDetail.BookTitle, new { @class = "control-label" })
        @Html.TextBoxFor(model => model.BookDetail.BookTitle, new { @class = "form-control" })
        @Html.ValidationMessageFor(model => model.BookDetail.BookTitle)

        @Html.LabelFor(model => model.BookDetail.BookAuthor, new { @class = "control-label" })
        @Html.TextBoxFor(model => model.BookDetail.BookAuthor, new { @class = "form-control" })
        @Html.ValidationMessageFor(model => model.BookDetail.BookAuthor)

        @Html.LabelFor(model => model.BookDetail.BookCategory, new { @class = "control-label" })
        @Html.TextBoxFor(model => model.BookDetail.BookCategory, new { @class = "form-control" })
        @Html.ValidationMessageFor(model => model.BookDetail.BookCategory)

        @Html.LabelFor(model => model.BookDetail.BookPublisher, new { @class = "control-label" })
        @Html.TextBoxFor(model => model.BookDetail.BookPublisher, new { @class = "form-control" })
        @Html.ValidationMessageFor(model => model.BookDetail.BookPublisher)
    </div>
    <div class="modal-footer">
        <button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
        <button type="submit" class="btn btn-primary">Save changes</button>
    </div>
}
Update Modal - EditBookDetails.cshtml.
@model BootstrapDemo.Models.BooksViewModel
<link rel="stylesheet" href="~/Content/bootstrap.min.css" />
<script type="text/javascript" src="~/Scripts/bootstrap.min.js"></script>

@using (Html.BeginForm("Edit","Home", FormMethod.Post))
{
    @Html.AntiForgeryToken()          
          
            <h3 class="modal-title">Edit Book Details</h3>
            <hr />
            @Html.ValidationSummary(true)
            @Html.HiddenFor(model => model.BookDetail.BookSerialNo)
                <div class="modal-body">                    
                    @Html.LabelFor(model => model.BookDetail.BookISBN, new { @class = "control-label" })
                    @Html.TextBoxFor(model => model.BookDetail.BookISBN, new { @class = "form-control" })
                    @Html.ValidationMessageFor(model => model.BookDetail.BookISBN)
                                        
                    @Html.LabelFor(model => model.BookDetail.BookTitle, new { @class = "control-label" })
                    @Html.TextBoxFor(model => model.BookDetail.BookTitle, new { @class = "form-control" })
                    @Html.ValidationMessageFor(model => model.BookDetail.BookTitle)
                                        
                    @Html.LabelFor(model => model.BookDetail.BookAuthor, new { @class = "control-label" })
                    @Html.TextBoxFor(model => model.BookDetail.BookAuthor, new { @class = "form-control" })
                    @Html.ValidationMessageFor(model => model.BookDetail.BookAuthor)
                                        
                    @Html.LabelFor(model => model.BookDetail.BookCategory, new { @class = "control-label" })
                    @Html.TextBoxFor(model => model.BookDetail.BookCategory, new { @class = "form-control" })
                    @Html.ValidationMessageFor(model => model.BookDetail.BookCategory)                                       
                    
                    @Html.LabelFor(model => model.BookDetail.BookPublisher, new { @class = "control-label" })
                    @Html.TextBoxFor(model => model.BookDetail.BookPublisher, new { @class = "form-control" })
                    @Html.ValidationMessageFor(model => model.BookDetail.BookPublisher)                    
                </div>
               <div class="modal-footer">
                    <button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
                    <button type="submit" class="btn btn-primary">Save changes</button>
                </div>
        
}
Delete Modal - DeleteBookDetails.cshtml.
@model BootstrapDemo.Models.BooksViewModel
<link rel="stylesheet" href="~/Content/bootstrap.min.css" />
<script type="text/javascript" src="~/Scripts/bootstrap.min.js"></script>

@using (Html.BeginForm("Delete", "Home", FormMethod.Post))
{
    @Html.AntiForgeryToken()

    <h3 class="modal-title">Delete this Book Item?</h3>
    <hr />
    @Html.ValidationSummary(true)
    @Html.HiddenFor(model => model.BookDetail.BookSerialNo)
    <div class="modal-body">
        @Html.LabelFor(model => model.BookDetail.BookISBN, new { @class = "control-label" })
        @Html.TextBoxFor(model => model.BookDetail.BookISBN, new { @class = "form-control", @readonly = "readonly" })

        @Html.LabelFor(model => model.BookDetail.BookTitle, new { @class = "control-label" })
        @Html.TextBoxFor(model => model.BookDetail.BookTitle, new { @class = "form-control", @readonly = "readonly" })

        @Html.LabelFor(model => model.BookDetail.BookAuthor, new { @class = "control-label" })
        @Html.TextBoxFor(model => model.BookDetail.BookAuthor, new { @class = "form-control", @readonly = "readonly" })

        @Html.LabelFor(model => model.BookDetail.BookCategory, new { @class = "control-label" })
        @Html.TextBoxFor(model => model.BookDetail.BookCategory, new { @class = "form-control", @readonly = "readonly" })

        @Html.LabelFor(model => model.BookDetail.BookPublisher, new { @class = "control-label" })
        @Html.TextBoxFor(model => model.BookDetail.BookPublisher, new { @class = "form-control ", @readonly = "readonly" })
    </div>
    <div class="modal-footer">
        <button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
        <button type="submit" class="btn btn-primary">Delete</button>
    </div>

}
Details Modal - BookDetails.cshtml.
@model BootstrapDemo.Models.BooksViewModel
<link rel="stylesheet" href="~/Content/bootstrap.min.css" />
<script type="text/javascript" src="~/Scripts/bootstrap.min.js"></script>

@using (Html.BeginForm("Details", "Home", FormMethod.Post))
{
    @Html.AntiForgeryToken()

    <h3 class="modal-title">Book Details</h3>
    <hr />
    @Html.ValidationSummary(true)
    @Html.HiddenFor(model => model.BookDetail.BookSerialNo)
    <div class="modal-body">
        @Html.LabelFor(model => model.BookDetail.BookISBN, new { @class = "control-label" })
        @Html.TextBoxFor(model => model.BookDetail.BookISBN, new { @class = "form-control", @readonly = "readonly" })

        @Html.LabelFor(model => model.BookDetail.BookTitle, new { @class = "control-label" })
        @Html.TextBoxFor(model => model.BookDetail.BookTitle, new { @class = "form-control", @readonly = "readonly" })

        @Html.LabelFor(model => model.BookDetail.BookAuthor, new { @class = "control-label" })
        @Html.TextBoxFor(model => model.BookDetail.BookAuthor, new { @class = "form-control", @readonly = "readonly" })

        @Html.LabelFor(model => model.BookDetail.BookCategory, new { @class = "control-label" })
        @Html.TextBoxFor(model => model.BookDetail.BookCategory, new { @class = "form-control", @readonly = "readonly" })

        @Html.LabelFor(model => model.BookDetail.BookPublisher, new { @class = "control-label" })
        @Html.TextBoxFor(model => model.BookDetail.BookPublisher, new { @class = "form-control ", @readonly = "readonly" })
    </div>
    <div class="modal-footer">
        <button type="button" class="btn btn-primary" data-dismiss="modal">Close</button>
    </div>

}
Output
Bootstrap Modal In ASP.NET MVC

Comments

  1. I spent a whole day trying to get Modal Bootstrap to work. Most tutorials are just way too complicated or lack all of the code necessary. This is example is simple and to the point... and works.

    ReplyDelete
    Replies
    1. Cool! Thanks for the kind words and I'm glad that my blog post was simple enough to get you started with Bootstrap Modal.

      Cheers!

      Delete
  2. how to check model validations ?

    ReplyDelete
    Replies
    1. Hi, you can apply the concept of Data Annotation to validate your model.

      Delete
  3. Thank you for the detailed explanation.

    ReplyDelete

Post a Comment

Donate

Popular Posts From This Blog

WPF CRUD Application Using DataGrid, MVVM Pattern, Entity Framework, And C#.NET

TypeScript Error Or Bug: The term 'tsc' is not recognized as the name of a cmdlet, function, script file, or operable program.

Invalid nested tag div found, expected closing tag input