Introduction
In the one of my previous article, I have explained how to create a simple master details entry form in asp.net MVC application, where I explained very simple one, without edit, delete functionality and all with text input field for details record for easy to understand.Here in this article, I will show you how to create an advanced master details entry form with following added features.
1. Cascade drop-down in the details record. an
2. Edit, delete functionality for edit/delete added details record.
This is our advance master-details entry form will look like.
You can see here in this picture, I have added 2 drop-down for implement cascade functionality in details record, a delete button on each row for delete added records and I have made each row editable for make it possible to edit already added details record if required.
Just follow the following steps in order to implement "Advance master details entry form in asp.net MVC".
Step - 1: Create New Project.
Step-2: Add a Database.
Go to Solution Explorer > Right Click on App_Data folder > Add > New item > Select SQL Server Database Under Data > Enter Database name > Add.
Step-3: Create tables in our database.
1. Categories
4. OrderDetails
double click on the database under app_data folder for open the database in server explorer > expand the database and Right click on Tables node > click on Add New Table > here we will write schema of the table for the table we want to create > now click on Update button for create the table and then again click on Update Database button. Do the same procedure for create all 4 tables.
Step-4: Add Entity Data Model.
A popup window will come (Entity Data Model Wizard) > Select Generate from database > Next >
Chose your data connection > select your database > next > Select tables > enter Model Namespace > Finish.
Step-5: Create an MVC Controller.
Here I have created a controller named "HomeController"
Step-6: Add an MVC action.
Here I have added "Index" Action to "Home" Controller. Please write this following codepublic ActionResult Index() { return View(); }
Step-7: Add view for that(here Index action) action.
Right Click on Action Method (here right click on Index action) > Add View... > Enter View Name > Select "Empty" under Template dropdown > > Add.HTML Code
@{ ViewBag.Title = "Index"; } <h2>Master details entry form</h2> <div class="container"> <div class="master"> <h2>Order</h2> <table class="table table-responsive"> <tr> <td>Order No</td> <td> <input type="text" id="orderNo" class="form-control" /> <span class="error">Order no required</span> </td> <td>Order Date</td> <td> <input type="text" id="orderDate" class="form-control" /> <span class="error">Valid order date required (ex: MM-dd-yyyy)</span> </td> </tr> <tr> <td>Description</td> <td colspan="3"> <textarea id="description" class="form-control"></textarea> </td> </tr> </table> </div> <div class="details"> <h2> Order items </h2> <table class="table table-responsive"> <tr> <td>Category</td> <td>Product</td> <td>Quantity</td> <td>Rate</td> <td> </td> </tr> <tr class="mycontainer" id="mainrow"> <td> <select id="productCategory" class="pc form-control" onchange="LoadProduct(this)"> <option>Select</option> </select> <span class="error">Select Category</span> </td> <td> <select id="product" class="product form-control"> <option>Select</option> </select> <span class="error">Select product</span> </td> <td> <input type="text" id="quantity" class="quantity form-control" /> <span class="error">Valid quantity required</span> </td> <td> <input type="text" id="rate" class="rate form-control" /> <span class="error">Valid rate required</span> </td> <td> <input type="button" id="add" value="add" style="width:80px" class="btn btn-success" /> </td> </tr> </table> <div id="orderItems"> <table class="table table-responsive" id="orderdetailsItems"></table> <span id="orderItemError" style="color:red"></span> </div> <div style="padding:10px 0; text-align:right"> <input id="submit" type="button" value="Save Order" class="btn btn-warning" style="padding:10px 20px" /> </div> </div> </div> <link href="~/Content/bootstrap.min.css" rel="stylesheet" /> <link rel="stylesheet" href="//code.jquery.com/ui/1.11.4/themes/smoothness/jquery-ui.css" /> <script src="//code.jquery.com/ui/1.11.4/jquery-ui.js"></script> <script src="~/Scripts/myScripts.js"></script> <script type="text/javascript"> $(function () { $('#orderDate').datepicker({ dateFormat: 'mm-dd-yy' }) }) </script> <style> span.error{ display:block; visibility:hidden; color:red; font-size:90%; } tr.error{ background-color:rgba(255,0,0,0.35); } </style>
<script src="https://code.jquery.com/jquery-2.2.4.min.js"></script>
Step-8: Add a javascript file, where we will add our client side code for save order to the database.
myScripts.js
var Categories = [] //fetch categories from database function LoadCategory(element) { if (Categories.length == 0) { //ajax function for fetch data $.ajax({ type: "GET", url: '/home/getProductCategories', success: function (data) { Categories = data; //render catagory renderCategory(element); } }) } else { //render catagory to the element renderCategory(element); } } function renderCategory(element) { var $ele = $(element); $ele.empty(); $ele.append($('<option/>').val('0').text('Select')); $.each(Categories, function (i, val) { $ele.append($('<option/>').val(val.CategoryID).text(val.CategortyName)); }) } //fetch products function LoadProduct(categoryDD) { $.ajax({ type: "GET", url: "/home/getProducts", data: { 'categoryID': $(categoryDD).val() }, success: function (data) { //render products to appropriate dropdown renderProduct($(categoryDD).parents('.mycontainer').find('select.product'), data); }, error: function (error) { console.log(error); } }) } function renderProduct(element, data) { //render product var $ele = $(element); $ele.empty(); $ele.append($('<option/>').val('0').text('Select')); $.each(data, function (i, val) { $ele.append($('<option/>').val(val.ProductID).text(val.ProductName)); }) } $(document).ready(function () { //Add button click event $('#add').click(function () { //validation and add order items var isAllValid = true; if ($('#productCategory').val() == "0") { isAllValid = false; $('#productCategory').siblings('span.error').css('visibility', 'visible'); } else { $('#productCategory').siblings('span.error').css('visibility', 'hidden'); } if ($('#product').val() == "0") { isAllValid = false; $('#product').siblings('span.error').css('visibility', 'visible'); } else { $('#product').siblings('span.error').css('visibility', 'hidden'); } if (!($('#quantity').val().trim() != '' && (parseInt($('#quantity').val()) || 0))) { isAllValid = false; $('#quantity').siblings('span.error').css('visibility', 'visible'); } else { $('#quantity').siblings('span.error').css('visibility', 'hidden'); } if (!($('#rate').val().trim() != '' && !isNaN($('#rate').val().trim()))) { isAllValid = false; $('#rate').siblings('span.error').css('visibility', 'visible'); } else { $('#rate').siblings('span.error').css('visibility', 'hidden'); } if (isAllValid) { var $newRow = $('#mainrow').clone().removeAttr('id'); $('.pc', $newRow).val($('#productCategory').val()); $('.product', $newRow).val($('#product').val()); //Replace add button with remove button $('#add', $newRow).addClass('remove').val('Remove').removeClass('btn-success').addClass('btn-danger'); //remove id attribute from new clone row $('#productCategory,#product,#quantity,#rate,#add', $newRow).removeAttr('id'); $('span.error', $newRow).remove(); //append clone row $('#orderdetailsItems').append($newRow); //clear select data $('#productCategory,#product').val('0'); $('#quantity,#rate').val(''); $('#orderItemError').empty(); } }) //remove button click event $('#orderdetailsItems').on('click', '.remove', function () { $(this).parents('tr').remove(); }); $('#submit').click(function () { var isAllValid = true; //validate order items $('#orderItemError').text(''); var list = []; var errorItemCount = 0; $('#orderdetailsItems tbody tr').each(function (index, ele) { if ( $('select.product', this).val() == "0" || (parseInt($('.quantity', this).val()) || 0) == 0 || $('.rate', this).val() == "" || isNaN($('.rate', this).val()) ) { errorItemCount++; $(this).addClass('error'); } else { var orderItem = { ProductID: $('select.product', this).val(), Quantity: parseInt($('.quantity', this).val()), Rate: parseFloat($('.rate', this).val()) } list.push(orderItem); } }) if (errorItemCount > 0) { $('#orderItemError').text(errorItemCount + " invalid entry in order item list."); isAllValid = false; } if (list.length == 0) { $('#orderItemError').text('At least 1 order item required.'); isAllValid = false; } if ($('#orderNo').val().trim() == '') { $('#orderNo').siblings('span.error').css('visibility', 'visible'); isAllValid = false; } else { $('#orderNo').siblings('span.error').css('visibility', 'hidden'); } if ($('#orderDate').val().trim() == '') { $('#orderDate').siblings('span.error').css('visibility', 'visible'); isAllValid = false; } else { $('#orderDate').siblings('span.error').css('visibility', 'hidden'); } if (isAllValid) { var data = { OrderNo: $('#orderNo').val().trim(), OrderDateString: $('#orderDate').val().trim(), Description: $('#description').val().trim(), OrderDetails: list } $(this).val('Please wait...'); $.ajax({ type: 'POST', url: '/home/save', data: JSON.stringify(data), contentType: 'application/json', success: function (data) { if (data.status) { alert('Successfully saved'); //here we will clear the form list = []; $('#orderNo,#orderDate,#description').val(''); $('#orderdetailsItems').empty(); } else { alert('Error'); } $('#submit').val('Save'); }, error: function (error) { console.log(error); $('#submit').val('Save'); } }); } }); }); LoadCategory($('#productCategory'));
Step-9: Add an MVC action into HomeController for fetch product categories from the database and return as JsonResult.
public JsonResult getProductCategories() { List<Category> categories = new List<Category>(); using (MyDatabaseEntities dc = new MyDatabaseEntities()) { categories = dc.Categories.OrderBy(a => a.CategortyName).ToList(); } return new JsonResult { Data = categories, JsonRequestBehavior = JsonRequestBehavior.AllowGet }; }
Step-10: Add an another MVC action into HomeController for fetch Products from the database based on selected category.
public JsonResult getProducts(int categoryID) { List<Product> products = new List<Product>(); using (MyDatabaseEntities dc = new MyDatabaseEntities()) { products = dc.Products.Where(a => a.CategoryID.Equals(categoryID)).OrderBy(a => a.ProductName).ToList(); } return new JsonResult { Data = products, JsonRequestBehavior = JsonRequestBehavior.AllowGet }; }
Step-11: Add an another action for save order information to the database.
[HttpPost] public JsonResult save(OrderMaster order) { bool status = false; DateTime dateOrg; var isValidDate = DateTime.TryParseExact(order.OrderDateString, "mm-dd-yyyy", null, System.Globalization.DateTimeStyles.None, out dateOrg); if (isValidDate) { order.OrderDate = dateOrg; } var isValidModel = TryUpdateModel(order); if (isValidModel) { using (MyDatabaseEntities dc = new MyDatabaseEntities()) { dc.OrderMasters.Add(order); dc.SaveChanges(); status = true; } } return new JsonResult { Data = new { status = status} }; }