Advance master details entry form in asp.net MVC



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".

Here In this article, I have used Visual Studio 2013

Step - 1: Create New Project.

Go to File > New > Project > ASP.NET  Web Application (under web) > Entry Application Name > Click OK > Select Empty template > Checked MVC (under "Add folders and core references for" option) > OK

Step-2: Add a Database.

Now I will create a database for our application. As this is a tutorial project, I will add a database in our application, here in the app_data folder.

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.

Now we have to create followings 4 tables in our database,  we will add one by one.

1. Categories

2. Products

3. OrderMaster


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.

Go to Solution Explorer > Right Click on Project name form Solution Explorer > Add > New item > Select ADO.net Entity Data Model under data > Enter model name > Add.
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.

Go to Solution Explorer > Right Click on Controllers folder form Solution Explorer > Add > Controller > Enter Controller name > Select Templete "empty MVC Controller"> Add.

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 code
public 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>&nbsp;</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>
Note: Don't forget to add jQuery library in your _Layout.cshtml page, add this below code
     <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.

Here in our application, I will add a javascript file into Scripts folder.
If you don't already have Scripts folder in your application, please first add Scripts folder and then add a javascript file into this folder.
Go to solution explorer > Right click on "Scripts" folder > Add > new Item > Select Javascrip file under Scripts > Eneter file name (here in my application it is "myScripts.js") > and then click on Add button.

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} };
}

Step-12: Run Application.

We have done all the steps. Now it's time to run the application. But before run the application, make sure we have already some Product Category and Products inserted in our database.

Hello ! My name is Sourav Mondal. I am a software developer working in Microsoft .NET technologies since 2010.

I like to share my working experience, research and knowledge through my site.

I love developing applications in Microsoft Technologies including Asp.Net webforms, mvc, winforms, c#.net, sql server, entity framework, Ajax, Jquery, web api, web service and more.