Friday 29 August 2014

CRUD & Multiple JQuery dialogs in Asp.Net MVC

In this article I am going to demonstrate how to achieve basic CRUD (Create, Read, Update and Delete) functionality by using jquery dialog(s) in asp.net mvc.  In this article I also these points:

1)      How to implement global validation by extending ActionFilterAttribute.

2)      Global custom message box by using jquery dialog.

3)      Use of webgrid control to display the list.

4)      Browse dialog by using jquery.ui.

5)      Confirmation message box on Delete.

Our final product will something look like this:

Download source code

I have taken an example of Product, SubCategory and Category by using Code first approach of entity framework, in which Category belongs to SubCategory and SubCategory belongs to Product. The database diagram should be something like below:

Prerequisites:

1)      Microsoft Visual Studio IDE

2)      SQL Server Database

3)      jquery-2.1.1

4)      jquery-ui-1.10.4

I am using visual studio 2012 and SQL Server 2008 database.

Let’s start by creating sample project and learn step by step:

1)      Open Microsoft Visual Studio 2012.

2)      Select New Project.

3)      Select ASP.NET MVC4 Web Application and Enter Name.

4)      Select Empty option from the Project template and View Engine as Razor.

5)      Add Category class in Models folder and replace with the following code:

public class Category

    {

        public int CategoryID { get; set; }

 

        [Required]

        [StringLength(50)]

        public string Name { get; set; }

    }

6)      Add SubCategory class and ad the following properties its configuration:

       public class SubCategory

       {

           public int SubCategoryID { get; set; }

 

           public virtual Category Category { get; set; }

 

           [Display(Name = "Category")]

           public int CategoryID { get; set; }

 

           [Required]

           [StringLength(50)]

           public string Name { get; set; }

}

7)      Like the above add Product class and replace with the following code:

 public class Product

    {

        public int ProductID { get; set; }

 

        [Required]

        [StringLength(50)]

        public string Name { get; set; }

 

        [Required]

        [StringLength(250)]

        public string Description { get; set; }

 

        public virtual SubCategory SubCategory { get; set; }

 

        [Display(Name = "Sub Category")]

        public int SubCategoryID { get; set; }

    }

8)      Now in order to test our application we need some sample data, so for its configuration add a ProductDbContextInitializer class which inherits DropCreateDatabaseIfModelChanges class of System.Data.Entity. Before doing this do not forget to install Entity framework because we are using Code first approach of Entity Framework in order to perform CRUD in database. It will automatically create database and tables if not already exist, we do not need to worry about database part. Easy way of including Entity Framework library in a project:

Go to Tools à Library Package Manager à Package Manager Console à Type install package entityframework

public class ProductDbContextInitializer : DropCreateDatabaseIfModelChanges<ProductDbContext>

{

        protected override void Seed(ProductDbContext context)

        {

            List<Category> categories = new List<Category>

            {

                new Category { Name = "Food" },

                new Category { Name = "Electronics" },

                new Category { Name = "Beverages" }

            };

 

            List<SubCategory> subcategories = new List<SubCategory>

            {

                new SubCategory { Name = "Vegetables", Category = categories.Find(m => m.Name.Equals("Food")) },

                new SubCategory { Name = "Fruits", Category = categories.Find(m => m.Name.Equals("Food")) },

                new SubCategory { Name = "Computer", Category = categories.Find(m => m.Name.Equals("Electronics")) },

                new SubCategory { Name = "Television", Category = categories.Find(m => m.Name.Equals("Electronics")) },

                new SubCategory { Name = "Cold Drinks", Category = categories.Find(m => m.Name.Equals("Beverages")) },

                new SubCategory { Name = "Beer", Category = categories.Find(m => m.Name.Equals("Beverages")) },

            };

 

            List<Product> products = new List<Product>

            {

                new Product { Name = "Wheat", Description = "Wheat", SubCategory = subcategories.Find(m => m.Name.Equals("Vegetables")) },

                new Product { Name = "Apple", Description = "Apple", SubCategory = subcategories.Find(m => m.Name.Equals("Fruits")) },

                new Product { Name = "Desktop", Description = "Desktop", SubCategory = subcategories.Find(m => m.Name.Equals("Computer")) },

                new Product { Name = "LG", Description = "LG", SubCategory = subcategories.Find(m => m.Name.Equals("Television")) },

                new Product { Name = "Sprite", Description = "Sprite", SubCategory = subcategories.Find(m => m.Name.Equals("Cold Drinks")) },

                new Product { Name = "Kingfisher", Description = "Kingfisher", SubCategory = subcategories.Find(m => m.Name.Equals("Beer")) },

            };

 

            categories.ForEach(m => context.Category.Add(m));

            subcategories.ForEach(m => context.SubCategory.Add(m));

            products.ForEach(m => context.Product.Add(m));

 

            context.SaveChanges();

 

            base.Seed(context);

        }

}

9)      Now add a DbContext class to add the tables and configuration. Please change the connection string part according to your database location and authentication:

    public class ProductDbContext : DbContext

    {

        public ProductDbContext()

            : base(nameOrConnectionString: "Data Source=SONY-VAIO\\SQLEXPRESS;Initial Catalog=ProductDbContext;Integrated Security=true;")

        {

            Database.SetInitializer(new ProductDbContextInitializer());

        }

 

        protected override void OnModelCreating(DbModelBuilder modelBuilder)

        {

            // Use singular table names

            modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();

 

            base.OnModelCreating(modelBuilder);

        }

 

        public DbSet<Product> Product { get; set; }

        public DbSet<Category> Category { get; set; }

        public DbSet<SubCategory> SubCategory { get; set; }

    }

 These nine simple steps helps you create and configure database with some sample data. The properties of the classes are understood and meaningful that’s why I do not waste my time on commenting or explaining the property.

Now let’s move towards our main concern of the article i.e. using multiple and managing jquery dialog(s):

1)      Include jquery  and jquery.ui in a project:

Go to Tools à Library Package Manager à Package Manager Console à

·         For jquery, type install package jquery

·         For jquery.ui, type install package jquery.ui.combined

·         For ajax, type install package jQuery.Ajax.Unobtrusive

2)      Add a Product controller in Controller folder and replace the code with the below. The functions of ProductController is pretty clear with its functions name.

    public class ProductController : Controller

    {

        ProductDbContext db = new ProductDbContext();

 

        public ActionResult Index()

        {

            return View();

        }

 

        public ActionResult List()

        {

            return PartialView("_ProductList", db.Product.ToList());

        }

 

        public PartialViewResult Add()

        {

            return PartialView("_Product");

        }

 

        [HttpPost, ValidationActionFilter]

        public PartialViewResult Add(Product model)

        {

            db.Product.Add(model);

            db.SaveChanges();

 

            return Add();

        }

 

        public PartialViewResult Edit(int id)

        {

            return PartialView("_Product", db.Product.Find(id));

        }

 

        [HttpPost, ValidationActionFilter]

        public PartialViewResult Edit(Product model)

        {

            var product = db.Product.Find(model.ProductID);

 

            db.Entry(product).CurrentValues.SetValues(model);

            db.SaveChanges();

 

            return Add();

        }

 

        [HttpPost]

        public JsonResult Delete(int id)

        {

            var product = db.Product.Find(id);

 

            db.Product.Remove(product);

            db.SaveChanges();

 

            return Json(new { success = true, message = "Record deleted successfully!" }, "application/json", JsonRequestBehavior.AllowGet);

        }

    }

3)      Add a ValidationActionFilter class to validate the data globally, you do not need to place ModelState.IsValid in every function before inserting or updating any record, just add ValidationActionFilter as attribute and all you have done it.

    public class ValidationActionFilter : ActionFilterAttribute

    {

        public override void OnActionExecuting(ActionExecutingContext filterContext)

        {

            var modelState = filterContext.Controller.ViewData.ModelState;

            var valueProvider = filterContext.Controller.ValueProvider;

 

            var keysWithNoIncomingValue = modelState.Keys.Where(x => !valueProvider.ContainsPrefix(x));

            foreach (var key in keysWithNoIncomingValue)

                modelState[key].Errors.Clear();

 

            if (!modelState.IsValid)

            {

                var errors = new JObject();

                foreach (var key in modelState.Keys)

                {

                    var state = modelState[key];

                    if (state.Errors.Any())

                    {

                        errors[key] = state.Errors.First().ErrorMessage;

                    }

                }

 

                filterContext.HttpContext.Response.ContentType = "application/json";

                filterContext.HttpContext.Response.StatusCode = (int)HttpStatusCode.BadRequest;

                filterContext.Result = new JsonResult

                {

                    Data = JsonConvert.SerializeObject(errors),

                    JsonRequestBehavior = JsonRequestBehavior.AllowGet,

                    ContentType = "application/json"

                };

            }

        }

    }

4)      Add a Index view of ProductController and replace the code with the following:

@{

    Layout = null;

}

 

<!DOCTYPE html>

 

<html>

<head>

    <meta name="viewport" content="width=device-width" />

    <title>Index</title>

    <link href="~/Content/themes/base/minified/jquery-ui.min.css" rel="stylesheet" />

    <link href="~/Content/Site.css" rel="stylesheet" />

    <link href="~/Content/GridStyle.css" rel="stylesheet" />

 

    <script src="~/Scripts/jquery-2.1.1.min.js"></script>

    <script src="~/Scripts/jquery-ui-1.10.4.min.js"></script>

    <script src="~/Scripts/jquery.unobtrusive-ajax.min.js"></script>

    <script src="~/Scripts/app.js"></script>

</head>

<body>

    <!-- this div is for custom message box-->

    <!-- start -->

    <div id="dialog" title="Alert message" style="display: none">

        <div class="ui-dialog-content ui-widget-content">

            <p>

                <span class="ui-icon ui-icon-alert" style="float: left; margin: 0 7px 20px 0"></span>

                <label id="lblMessage">

                </label>

            </p>

        </div>

    </div>

    <!--  end -->

 

    <div id="productList">

        @using (Ajax.BeginForm("List", new AjaxOptions { }))

        {

            <div>

                @Html.ActionLink("Add Product", "Add", null, new { @class = "dialog button", @title = "Add Product" })

            </div>

            <div id="product-grid">

 

            </div>       

        }

    </div>

    <script>

        $(function () {

 

            $("a.button").button();

        });

    </script>

</body>

</html>

5)      Add a partial view _ProductList in Views/Product and replace the code with the following in order to display the Product list in webgrid:

@model IEnumerable<ManagingMultijQueryForm.Models.Product>

 

@{

    var grid = new WebGrid(

        Model, rowsPerPage: 10,

        defaultSort: "Name", ajaxUpdateContainerId: "product-grid");

}

 

@grid.GetHtml(

            fillEmptyRows: false,

            mode: WebGridPagerModes.All,

 

            tableStyle: "webgrid-table",

            headerStyle: "webgrid-header",

            footerStyle: "webgrid-footer",

            alternatingRowStyle: "webgrid-alternating-row",

            rowStyle: "webgrid-row-style",

 

            columns: grid.Columns

            (

                grid.Column("Name", header: "Name " + WebGridSortHelper.SortDirection(null, ref grid, "Name")),

                grid.Column("Description", header: "Description " + WebGridSortHelper.SortDirection(null, ref grid, "Description")),

                grid.Column("SubCategory.Name", header: "SubCategory " + WebGridSortHelper.SortDirection(null, ref grid, "SubCategory.Name")),

                grid.Column("SubCategory.Category.Name", header: "Category " + WebGridSortHelper.SortDirection(null, ref grid, "SubCategory.Category.Name")),

                grid.Column("", format: (a) => @Html.ActionLink("Edit", "Edit", "Product", new { @id = a.ProductID }, new { @class = "dialog", @title = "Edit" }), header: ""),

                grid.Column("", format: (a) => @Html.ActionLink("Delete", "Delete", "Product", new { @id = a.ProductID }, new { @class = "delete", @title = "Delete" }), header: "")

            )

)

6)      Create a WebGridSortHelper at the root level of project to customize the sort display in webgrid:

namespace Helpers

{

    public static class WebGridSortHelper

    {

        public static MvcHtmlString SortDirection(this HtmlHelper helper, ref WebGrid grid, string columnName)

        {

            string html = "";

            if (grid.SortColumn == columnName && grid.SortDirection == System.Web.Helpers.SortDirection.Ascending)

                html = "▲";

            else if (grid.SortColumn == columnName && grid.SortDirection == System.Web.Helpers.SortDirection.Descending)

                html = "▼";

            else

                html = "";

 

            return MvcHtmlString.Create(html);

        }

    }

}

In Views/web.config add the following namespace in the namespaces tag, it help you to easily access the namespace classes in views.

<add namespace="Helpers" />

7)      Add a partial view _Product in Views/Product which is used to add/edit a product information:

@model ManagingMultijQueryForm.Models.Product

 

@using (Ajax.BeginForm(new AjaxOptions { OnSuccess = "closeDialog();updateSection();", OnFailure = "onAjaxFailure" }))

{

    @Html.ValidationSummary(true)

    if (Model != null)

    {

        @Html.HiddenFor(m => m.ProductID)

    }

   

    <fieldset>

        <legend>Product</legend>

 

        <div class="editor-label">

            @Html.LabelFor(model => model.Name)

        </div>

        <div class="editor-field">

            @Html.EditorFor(model => model.Name)

            @Html.ValidationMessageFor(model => model.Name)

        </div>

 

        <div class="editor-label">

            @Html.LabelFor(model => model.Description)

        </div>

        <div class="editor-field">

            @Html.EditorFor(model => model.Description)

            @Html.ValidationMessageFor(model => model.Description)

        </div>

 

        <div class="editor-label">

            @Html.LabelFor(model => model.SubCategoryID)

        </div>

        <div class="editor-field">

            <input type="text" disabled="disabled" value="@(Model != null ? Model.SubCategory.Name : "")" />

            @Html.HiddenFor(model => model.SubCategoryID)

            @Html.ValidationMessageFor(model => model.SubCategoryID)

            @Html.ImageActionLink(Url.Content("~/Content/Images/Icons/search.png"), "Browse Sub Category", "List", "SubCategory", null, new { @class = "dialog", @title = "Sub Category" }, new { border = 0 })

        </div>

 

        <p>

            <input type="submit" value="@(Model != null ? "Update" : "Create")" />

        </p>

    </fieldset>

    <script>

        $(function () {

            $("img").button();

        });

    </script>

}

 

8)      Now create app.js file in Scripts folder. In this file we put our jquery or javascript  code. To handle global validation message.

9)      Add onAjaxFailure method to handle global validation message, which is given below:

function onAjaxFailure(response) {

 

    var data = $.parseJSON(response.responseJSON);

    var content = "<ul>";

    for (var key in data) {

        content += "<ol>" + data[key] + "</ol>";

    }

 

    content += "</ul>"

 

    showMessageBox({

        title: "Error",

        content: content,

        btn1text: "Ok"

    });

}

10)   The above method (onAjaxFailure) also uses showMessageBox method in order to display the custom message box by using jquery.ui dialog, so create showMessageBox function in the same app.js:

function showMessageBox(params) {

 

    var btn1css;

    var btn2css;

 

    if (params.btn1text) {

        btn1css = "showcss";

    } else {

        btn1css = "hidecss";

    }

 

    if (params.btn2text) {

        btn2css = "showcss";

    } else {

        btn2css = "hidecss";

    }

 

    $("#lblMessage").html(params.content);

 

    $("#dialog").dialog({

        resizable: false,

        title: params.title,

        modal: true,

        width: 'auto',

        height: 'auto',

        bgiframe: false,

        hide: { effect: 'scale', duration: 400 },

        buttons: [

            {

                text: params.btn1text,

                "class": btn1css,

                click: function () {

                    if (params.functionText) { window[params.functionText](params.parameterList); } // Call function after clicking on button.

                    $("#dialog").dialog('close');

                }

            },

            {

                text: params.btn2text,

                "class": btn2css,

                click: function () {

                    $("#dialog").dialog('close');

                }

            }

        ]

    });

}

11)   To add/choose/delete a product, subcategory and category we need dialogs, so for dialogs, first create these two global variables in app.js:

var arrDialogRef = [];

var curDialogRef = null;

These variables is used for storing the references of dialog, arrDialogRef stores a reference of dialogs on dialog open event and release the reference on close event of dialog and curDialogRef always have a reference of most recent open dialog.

12)   To open a add/choose dialog and for confirmation message on delete and for display the list of products on form load, update the following code in app.js:

// on load of application

$(function () {

 

    curDialogRef = $("#productList");

 

    $(document).on("click", "a.dialog", function (event) {

 

        event.preventDefault();

 

        var currentLinkRef = this;

        var url = $(this).attr("href");

        var title = $(this).attr("title");

        var dialog = $("<div></div>");

 

        $(dialog)

            .load(url)

            .dialog({

                title: title,

                autoOpen: false,

                resizable: false,

                height: 'auto',

                width: '50%',

                minWidth: '400',

                minHeight: '400',

                show: { effect: 'drop', direction: "up" },

                modal: true,

                draggable: true,

                open: function (event, ui) {

 

                    var me = this;

 

                    // storing reference

                    arrDialogRef.push({

                        Source: curDialogRef,// parent dialog reference

                        Destination: me, // child or open dialog refernce

                        Element: currentLinkRef, // current link reference

                    });

 

                    curDialogRef = me;

                },

                close: function (event, ui) {

 

                    // releasing resource of dialog on close event

                    var me = this;

                    $.each(arrDialogRef, function (i, val) {

 

                        if (val.Destination == me) {

                            curDialogRef = val.Source;

                            arrDialogRef.splice(i, 1);

                            return false;

                        }

                    });

 

                    $(this).empty().dialog('destroy').remove();

                }

            });

 

        $(dialog).dialog('open');

    });

 

    $(document).on("click", "a.delete", function (event) {

 

        event.preventDefault();

        var url = $(this).attr("href");

 

        showMessageBox({

            title: "Question",

            content: "Are you sure want to delete this record?",

            btn1text: "Yes",

            btn2text: "No",

            functionText: "makeAjaxCall",

            parameterList: {

                url: url,

                callback: 'onAjaxDeleteSuccess'

            }

        });

    });

 

    updateSection(); // update the section of product list on load

});

13)   Now in order to display or update the data in a list, create updateSection method in app.js:

function updateSection() {

   

    var source = null;

    var updateContainerId = "";

    var ref = getParentDialogRef();

    if (ref && ref.Destination) {

        source = ref.Destination;

        updateContainerId = source;

    }

    else if (curDialogRef) {

        source = curDialogRef;

        updateContainerId = $("#product-grid");

    }

 

    var url = $(source).find("form").attr("action");

 

    if (url) {

 

        $.get(url, function (data) {

           

            $(updateContainerId).html(data);

        });

    }

}

14)   Create makeAjaxCall (to make ajax call) function which is used for making delete request via ajax after delete confirmation:

function makeAjaxCall(params) {

 

    $.ajax({

        type: 'POST',

        url: params.url,

        data: params.parameters,

        dataType: 'json',

        success: function (response) {

            if (response.success) {

                eval(params.callback + '(response)');

            }

        }

    })

}

 

 

15)   Add a SubCategory controller and add some following necessary methods:

    public class SubCategoryController : Controller

    {

        ProductDbContext db = new ProductDbContext();

 

        public ActionResult Index()

        {

            return View();

        }

 

        public JsonResult Get(int id)

        {

            var subcategory  = db.SubCategory.Find(id);

 

            return Json(new { Id = subcategory.SubCategoryID, Name = subcategory.Name }, "application/json", JsonRequestBehavior.AllowGet);

        }

 

        public PartialViewResult List()

        {

            return PartialView("_SubCategoryList", db.SubCategory.ToList());

        }

 

        [HttpPost]

        public PartialViewResult List(string s)

        {

            return PartialView("_SubCategoryList", db.SubCategory.Where(m => m.Name.Contains(s) || m.Category.Name.Contains(s)).ToList());

        }

 

        public PartialViewResult Add()

        {

            return PartialView("_SubCategory");

        }

 

        [HttpPost, ValidationActionFilter]

        public PartialViewResult Add(SubCategory model)

        {

            db.SubCategory.Add(model);

            db.SaveChanges();

 

            return Add();

        }

 

        [HttpPost]

        public JsonResult Delete(int id)

        {

            var subcategory = db.SubCategory.Find(id);

 

            db.SubCategory.Remove(subcategory);

            db.SaveChanges();

 

            return Json(new { success = true, message = "Record deleted successfully!" }, "application/json", JsonRequestBehavior.AllowGet);

        }

    }

16)   Add a partial view _SubCategoryList in Views/SubCategory folder and replace the code as given below:

@model IEnumerable<ManagingMultijQueryForm.Models.SubCategory>

 

@using (Ajax.BeginForm(new AjaxOptions { OnSuccess = "updateSearchContainer" }))

{

    <p>

        @Html.TextBox("s")

        <input type="submit" value="Search" />

        @Html.ActionLink("Create New", "Add", "SubCategory", null, new { @class = "dialog ahref", @title = "Add Sub Category" })

    </p>

}

@{

    var grid = new WebGrid(

        Model, rowsPerPage: 10,

        defaultSort: "Name",

        ajaxUpdateContainerId: "subcategoryList");

}

 

<div id="subcategoryList">

 

    @grid.GetHtml(

            fillEmptyRows: false,

            mode: WebGridPagerModes.All,

 

            tableStyle: "webgrid-table",

            headerStyle: "webgrid-header",

            footerStyle: "webgrid-footer",

            alternatingRowStyle: "webgrid-alternating-row",

            rowStyle: "webgrid-row-style",

 

            columns: grid.Columns

            (

                grid.Column("", format: (a) => @Ajax.ActionLink("Select", "Get", "SubCategory", new { @id = a.SubCategoryID }, new AjaxOptions { OnSuccess = "updateContainer" }), header: ""),

                grid.Column("Name", header: "Name " + WebGridSortHelper.SortDirection(null, ref grid, "Name")),

                grid.Column("Category.Name", header: "Category " + WebGridSortHelper.SortDirection(null, ref grid, "Category.Name")),

                grid.Column("", format: (a) => @Html.ActionLink("Delete", "Delete", "SubCategory", new { @id = a.SubCategoryID }, new { @class = "delete", @title = "Delete" }), header: "")

            )

        )

</div>

 

<script>

    $("a.ahref, input[type='submit']").button();

</script>

 

17)   Add a partial view _SubCategory in Views/SubCategory, set the code as given below:

@model ManagingMultijQueryForm.Models.SubCategory

 

@using (Ajax.BeginForm(new AjaxOptions { OnSuccess = "closeDialog();updateSection();", OnFailure = "onAjaxFailure" }))

{

    @Html.ValidationSummary(true)

 

    <fieldset>

        <legend>SubCategory</legend>

 

        <div class="editor-label">

            @Html.LabelFor(model => model.Name)

        </div>

        <div class="editor-field">

            @Html.EditorFor(model => model.Name)

            @Html.ValidationMessageFor(model => model.Name)

        </div>

 

        <div class="editor-label">

            @Html.LabelFor(model => model.CategoryID)

        </div>

        <div class="editor-field">

            <input type="text" value="@(Model != null ? Model.Category.Name : "")" disabled="disabled" />

            @Html.HiddenFor(model => model.CategoryID)

            @Html.ValidationMessageFor(model => model.CategoryID)

            @Html.ImageActionLink(Url.Content("~/Content/Images/Icons/search.png"), "Browse Category", "List", "Category", null, new { @class = "dialog", @title = "Category", }, new { border = 0 })

        </div>

 

        <p>

            <input type="submit" value="Create" />

        </p>

    </fieldset>

    <script>

        $(function () {

            $("img").button();

        });

    </script>

}

 

18)   Add the closeDialog method to close the dialog in app.js:

function getParentDialogRef() {

    if (arrDialogRef && arrDialogRef.length > 0) {

        return arrDialogRef[arrDialogRef.length - 1];

    }

 

    return null;

}

 

function closeDialog() {

 

    var ref = getParentDialogRef();

    if (ref) {

        $(ref.Destination).dialog("close");

    }

}

19)   Add updateContainer method in app.js to update the parent section, for e.g. If you browse a sub category list and select a subcategory, then the subcategory section of product will be updated.

function updateContainer(response) {

    var ref = getParentDialogRef();

 

    if (ref) {

 

        $(ref.Element).parent().find("input[type='hidden']").val(response.Id);

        $(ref.Element).parent().find("input[type='text']").val(response.Name);

 

        closeDialog();

    }

}

20)   Create updateSearchContainer method in app.js which is used for updating the content in case of search performed on modal dialog(s):

function updateSearchContainer(response) {

    var ref = getParentDialogRef();

    if (ref) {

        $(ref.Destination).html(response);

    }

}

 

21)   Create last and final Category controller, add the methods given below:

public class CategoryController : Controller

    {

        ProductDbContext db = new ProductDbContext();

 

        public ActionResult Index()

        {

            return View();

        }

 

        public JsonResult Get(int id)

        {

            var category = db.Category.Find(id);

 

            return Json(new { Id = category.CategoryID, Name = category.Name }, "application/json", JsonRequestBehavior.AllowGet);

        }

 

        public PartialViewResult List()

        {

            return PartialView("_CategoryList", db.Category.ToList());

        }

 

        [HttpPost]

        public PartialViewResult List(string s)

        {

            return PartialView("_CategoryList", db.Category.Where(m => m.Name.Contains(s)).ToList());

        }

 

        public PartialViewResult Add()

        {

            return PartialView("_Category");

        }

 

        [HttpPost, ValidationActionFilter]

        public PartialViewResult Add(Category model)

        {

            db.Category.Add(model);

            db.SaveChanges();

 

            return Add();

        }

 

        [HttpPost]

        public JsonResult Delete(int id)

        {

            var category = db.Category.Find(id);

 

            db.Category.Remove(category);

            db.SaveChanges();

 

            return Json(new { success = true, message = "Record deleted successfully!" }, "application/json", JsonRequestBehavior.AllowGet);

        }

    }

22)   Create a partial view _CategoryList in Views/Category folder:

@model IEnumerable<ManagingMultijQueryForm.Models.Category>

 

@using (Ajax.BeginForm(new AjaxOptions { OnSuccess = "updateSearchContainer" }))

{

    <p>

        @Html.TextBox("s")

        <input type="submit" value="Search" />

        @Html.ActionLink("Create New", "Add", "Category", null, new { @class = "dialog ahref", @title = "Add Category" })

    </p>

}

@{

    var grid = new WebGrid(

        Model, rowsPerPage: 5,

        defaultSort: "Name",

        ajaxUpdateContainerId: "categoryList");

}

 

<div id="categoryList">

    @grid.GetHtml(

            fillEmptyRows: false,

            mode: WebGridPagerModes.All,

 

            tableStyle: "webgrid-table",

            headerStyle: "webgrid-header",

            footerStyle: "webgrid-footer",

            alternatingRowStyle: "webgrid-alternating-row",

            rowStyle: "webgrid-row-style",

 

            columns: grid.Columns

            (

                grid.Column("", format: (a) => @Ajax.ActionLink("Select", "Get", "Category", new { @id = a.CategoryID }, new AjaxOptions { OnSuccess = "updateContainer" }), header: ""),

                grid.Column("Name", header: "Name " + WebGridSortHelper.SortDirection(null, ref grid, "Name")),

                grid.Column("", format: (a) => @Html.ActionLink("Delete", "Delete", "Category", new { @id = a.CategoryID }, new { @class = "delete", @title = "Delete" }), header: "")

            )

        )

</div>

<script>

    $("a.ahref, input[type='submit']").button();

</script>

23)   Create a partial view _Category in Views/Category folder:

@model ManagingMultijQueryForm.Models.Category

 

@using (Ajax.BeginForm(new AjaxOptions { OnSuccess = "closeDialog();updateSection();", OnFailure = "onAjaxFailure" }))

{

    @Html.ValidationSummary(true)

 

    <fieldset>

        <legend>Category</legend>

 

        <div class="editor-label">

            @Html.LabelFor(model => model.Name)

        </div>

        <div class="editor-field">

            @Html.EditorFor(model => model.Name)

            @Html.ValidationMessageFor(model => model.Name)

        </div>

 

        <p>

            <input type="submit" value="Create" />

        </p>

    </fieldset>

}

 

Now run or debug your application, I hope it works fine. You have any question or doubt regarding this article or sample, you can ask.

I have attached source code too. If you find any mistake or suggestion for this sample, please must share.

Download source code

Read Next part of this article Repository and Unit of Work with Entity Framework.

10 comments:

Unknown said...

Hi,

I have two questions about your example. First how to deal with a scenario of master /(multiple) details (I have in mind a relation such Jobs and JobDetails, JobRequirements, JobSkills etc.)
And second, is possible to do this inline instead of pop-up? (I have in mind an wizard like approach: step1 -> Jobs, step2 -> JobDetails, step3 -> JobRequirements and so on)

Thank you

Rohit said...

If you need step wise wizard like next previous. Then u need to have navigation instead of modal popup.

You can use sammyjs for client side navigation.

skylancer3id said...

Thanks for the great article, but there is a problem after the product is displayed and do sorting, the product grid is disappear, why? could you help?

Rohit said...

Hi skylancer3id!

This is a problem. In order to fix this just add a div container with id ("product-grid-table") in _ProductList.cshtml

div id="product-grid-table"
//inside this put grid binding code
// replace ajaxUpdateContainerId with this ajaxUpdateContainerId: "product-grid-table"
div

Mary Brown said...

very useful article


Online MVC Training

The Beast said...

Thanks for the great article, but there is a problem after the product is displayed and do sorting, the product grid is disappear, why? could you help?

The Beast said...


This is a problem. In order to fix this just add a div container with id ("product-grid-table") in _ProductList.cshtml

Rohit said...

Yes "The Beast".

Anonymous said...

Hi Rohit! I really like your article, I am newbie for web mvc api. Now I am looking your article related to Multiple checkedboxlist in MVC. Do you have any post related?

Vijay Saklani said...

Nice article..aspmantra