top button
Flag Notify
    Connect to us
      Site Registration

Site Registration

Select All And Delete Using ASP.NET MVC And JQuery Ajax

+4 votes
842 views

Sometimes you need to select records for certain action using checkboxes. For example, you may select records for deleting and then delete them from the database. Consider the following screen shot that shows such an example in action.

image

As you can see there are two ways to select records for deletion:

  • You select checkboxes for rows to be deleted individually.
  • You can check the checkbox placed in the header row to select all the rows. This checkbox toggles the checked state of the other checkboxes.

Once selected you can click on the Delete Selected Customers button to actually delete the records.

Implementing such a functionality is straightforward using ASP.NET MVC, jQuery and Ajax. Let's see how.

As an example we will use Customers table of the Northwind database for this example. You will need to create a model class for the Customers table using EF code first. The Customer class is shown below:

public partial class Customer
{
    [StringLength(5)]
    public string CustomerID { get; set; }

    [Required]
    [StringLength(40)]
    public string CompanyName { get; set; }

    [StringLength(30)]
    public string ContactName { get; set; }

    [StringLength(30)]
    public string ContactTitle { get; set; }

    [StringLength(60)]
    public string Address { get; set; }

    [StringLength(15)]
    public string City { get; set; }

    [StringLength(15)]
    public string Region { get; set; }

    [StringLength(10)]
    public string PostalCode { get; set; }

    [StringLength(15)]
    public string Country { get; set; }

    [StringLength(24)]
    public string Phone { get; set; }

    [StringLength(24)]
    public string Fax { get; set; }
}

The NorthwindDbContext - the DbContext of our model - is shown below:

public partial class NorthwindDbContext : DbContext
{
    public NorthwindDbContext()
        : base("name=NorthwindDbContext")
    {
    }

    public virtual DbSet<Customer> Customers { get; set; }

    protected override void OnModelCreating
                (DbModelBuilder modelBuilder)
    {
        modelBuilder.Entity<Customer>()
            .Property(e => e.CustomerID)
            .IsFixedLength();
    }
}

Notice that the NorthwindDbContext assumes that the database connection string is stored in web.config with a name of NorthwindDbContext.

Now add HomeController and write Index() and Delete() actions as shown below:

public ActionResult Index()
{
    using (NorthwindDbContext db = 
                  new NorthwindDbContext())
    {
        var query = from c in db.Customers
                    select c;
        return View(query.ToList());
    }
}

public ActionResult Delete(string[] customerIDs)
{
    using (NorthwindDbContext db = 
                        ew NorthwindDbContext())
    {
        foreach (string customerID in customerIDs)
        {
            Customer obj = db.Customers.Find(customerID);
            db.Customers.Remove(obj);
        }
        db.SaveChanges();
        return Json("All the customers 
                     deleted successfully!");
    }
}

The code from the Index() action simply picks all the customers from the Customers table and passes them to the Index view for display.

The Delete() action takes a single parameter - array of CustomerIDs to be deleted. The Delete() action will be called through client side jQuery code and while calling the array will be passed to it. The Delete() action simply iterates through the customerIDs array and one-by-one deletes the customers from the database. Finally, a success message is sent back to the caller in JSON format.

Now add Index view and also add a <script> reference to the jQuery library. Then add the following markup in the Index view.

@model List<SelectAllDeleteDemo.Models.Customer>
...
...
<body>
    <h1>List of Customers</h1>
    <input type="button" id="delete" 
         value="Delete Selected Customers" />
    <br /><br />
    <table border="1" cellpadding="10">
        <tr>
            <th><input type="checkbox" id="checkAll"/></th>
            <th>CustomerID</th>
            <th>CompanyName</th>
            <th>Country</th>
        </tr>
        @foreach(var item in Model)
        {
            <tr>
                <td><input type="checkbox" class="checkBox" 
                     value="@item.CustomerID" /></td>
                <td>@item.CustomerID</td>
                <td>@item.CompanyName</td>
                <td>@item.Country</td>
            </tr>
        }
    </table>
</body>
...

Notice a few things about this markup:

  • The customer data - CustomerID, CompanyName and Country - is displayed in a table.
  • The header row contains a checkbox whose ID is checkAll
  • Each table row contains a checkbox whose class attribute is set to checkBox. And its value is set to the CustomerID of that row.
  • The button above the table is used to initiate the delete operation and its ID is delete.

Now add a <script> block and write the following jQuery code:

$(document).ready(function () {

    $("#checkAll").click(function () {
        $(".checkBox").prop('checked', 
            $(this).prop('checked'));
    });

    $("#delete").click(function () {
        var selectedIDs = new Array();
        $('input:checkbox.checkBox').each(function () {
            if ($(this).prop('checked')) {
                selectedIDs.push($(this).val());
            }
        });

        var options = {};
        options.url = "/home/delete";
        options.type = "POST";
        options.data = JSON.stringify(selectedIDs);
        options.contentType = "application/json";
        options.dataType = "json";
        options.success = function (msg) {
            alert(msg);
        };
        options.error = function () {
            alert("Error while deleting the records!");
        };
        $.ajax(options);

    });
});

The code wires click event handlers for the checkAll checkbox and the delete button. The click event handler of the checkAll checkbox toggles the checked state of all the checkboxes. This is done by selecting the checkboxes using the jQuery class selector. The checkboxes whose class attribute is checkBox are matched and their checked property is toggled. Notice the use of prop() method to do this.

The click event handler of the delete button declares an array variable to store the selected CustomerIDs. It then selects all the checkboxes with CSS class of checkBox. The each() method iterates through these checkboxes. If a checkbox is checked its value is pushed into the array. This way we get all the CustomerIDs into the selectedIDs array. The success callback simply displays the success message returned from the Delete() action.

Then options object is created to hold all the Ajax configuration properties. Notice that url property points to the Delete() action and data property holds the JSON version of the selectedIDs array. Finally, $.ajax() is used to make the Ajax call.

That's it! Run the application and test the functionality.

posted Oct 13, 2016 by Shivaranjini

  Promote This Article
Facebook Share Button Twitter Share Button LinkedIn Share Button


Related Articles

In my previous article I illustrated how jQuery can be used to select and delete records in an ASP.NET MVC application. A few readers asked how the same can be accomplished using AngularJS instead of jQuery. This article shows just that.

Recollect how our Index view looks like and how it allows you to select all rows through the header checkbox or individual rows through the respective checkboxes.

image

As you can see there are two ways to select records for deletion:

  • You select checkboxes for rows to be deleted individually.
  • You can check the checkbox placed in the header row to select all the rows. This checkbox toggles the checked state of the other checkboxes.

Once selected you can click on the Delete Selected Customers button to actually delete the records.

In order to implement such a functionality using ASP.NET MVC and AngularJS you will need to modify the Index view as shown below:

@model List<SelectAllDeleteDemo.Models.Customer>
...
...
<html ng-app>
<body ng-controller="MyController">
    <h1>List of Customers</h1>
    <input type="button" id="delete" 
           value="Delete Selected Customers" 
           ng-click="DeleteSelected()" />
    <br /><br />
    <table border="1" cellpadding="11">
        <tr>
            <th><input type="checkbox" id="checkAll" 
                 ng-click="ToggleSelectAll()" /></th>
            <th>CustomerID</th>
            <th>CompanyName</th>
            <th>Country</th>
        </tr>
        @{ int i = 0;}
        @foreach (var item in Model)
        {
            <tr>
                <td><input type="checkbox" 
                     class="checkBox" 
                     value="@item.CustomerID" 
                     ng-checked="selectAll" 
                     ng-model="customerIDs[@i].selected" /></td>
                <td>@item.CustomerID</td>
                <td>@item.CompanyName</td>
                <td>@item.Country</td>
            </tr>
            i++;
        }
    </table>
</body>
</html>

Notice a few things about this markup:

  • The <html> tag has ng-app attribute indicating that this page is an AngularJS application.
  • The <body> tag specifies ng-controller attribute to be MyController. You will write this controller shortly.
  • The Delete Selected Customers button wires a click event handler using ng-click attribute and it is set to DeleteSelected().
  • The header checkbox wires click event handler using ng-click to ToggleSelectAll(). This event handler is responsible for toggling the state of all the checkboxes.
  • The checkboxes in individual rows are bound with selected property of objects stored in customerIDs array. This part will be clear once you see the code shortly.

Now add a script reference to AngularJS and also add a <script> block. Then write the following jQuery code:

<script src="~/Scripts/angular-1.2.js"></script>
<script>
    function MyController($scope, $http) {
        $scope.selectAll = false;
        $scope.customerIDs = new Array();

        @foreach (var item in Model)
        {
            @:$scope.customerIDs.push({ 'customerID': 
             '@item.CustomerID', 'selected': false });
        }

        $scope.DeleteSelected = function () {
            var selectedIDs = new Array();
            angular.forEach
            ($scope.customerIDs, function (item) {
                if(item.selected)
                {
                    selectedIDs.push(item.customerID);
                }
            });
            var promise = $http.post
                    ("/home/delete", selectedIDs);
            promise.success(function (msg) {
                alert(msg);
            }).error(function () {
                alert("Error");
            });
        }

        $scope.ToggleSelectAll = function () {
            $scope.selectAll = !$scope.selectAll;
            angular.forEach(
                $scope.customerIDs, function (item) {
                item.selected = $scope.selectAll;
            });
        }
    }
</script>

The code consists of MyController function - the AngularJS controller - with two model properties and two event handlers.

The selectAll model property is initially set to false indicating that all the checkboxes are unchecked. Recollect that selectAll property is bound with the ng-checked attribute of individual checkboxes. The customerIDs array stores JavaScript objects, each holding a CustomerID and its checked state. Notice how this array is filled by dynamically emitting push() script calls through Razor code block. Recollect that checkboxes from the individual rows are model bound with customerIDs[i].selected property. This way checking or unchecking a checkbox individually (rather than header checkbox) toggles the corresponding entry from the customerIDs array.

The DeleteSelected() function handles the click event of the Delete Selected Customers button. Inside, the code iterates through the customerIDs array using forEach() and determines whether selected property is true or false. If the selected property is true, that customerID is pushed into a local array - selectedIDs. This way you get an array of CustomerIDs that are checked in the table. Then the code makes an Ajax call to the /home/delete action method. This is done using the post() method of $http. The post() method takes two parameters - url of the resource to be invoked and data to be sent along with the request. In this case you pass the selectedIDs array to the Delete() action method. The success() and error() method simply wire the respective callbacks to the promise object returned by the post() method.

The ToggleSelectAll() function acts as the click event handler for the header checkbox. Inside, it toggles the selectAll model property. The code then iterates through all the elements of the customerIDs array and set selected property to the value of selectAll. This way all the checkboxes from the table rows toggle their state.

That's it! Run the application and test the functionality.

READ MORE

In data entry forms involving textboxes with predictable values one can use autocomplete to assist user pick an existing value. HTML5 introduces <datalist> element that can come handy while implementing autocomplete. The <datalist> element holds a list of options and can be attached with a textbox using list attribute. By adding a bit of jQuery Ajax you can dynamically populate the options in a <datalist>. This article shows you how to do just that.

HTML5 <datalist> element is used as shown in the following markup.

<input type="text" list="datalist1" />

<datalist id="datalist1">
  <option value="US" label="United States" />
  <option value="UK" label="United Kingdom" />
  <option value="IN" label="India" />
</datalist>

At runtime the above markup shows an autocomplete as shown below:

image

Once you select an option from the list, the textbox is filled with the value. The <datalist> and its <option> elements are statically placed in the above markup. If you wish to dynamically populate the <datalist> based on the value entered in the textbox, you need to make an Ajax call to the server and fetch the required data.

Consider the following HTML markup that has a textbox and an empty <datalist>.

<h1>Autocomplete Example</h1>
<input id="companyName" list="companyList" />
<datalist id="companyList"></datalist>

To dynamically populate the <datalist> you can add the following jQuery code:

$(document).ready(function () {
    $("#companyName").on("input", function () {
        var options = {};
        options.url = "/home/getcompanylist";
        options.type = "GET";
        options.data = { "criteria": $("#companyName").val() };
        options.dataType = "json";
        options.success = function (data) {
            $("#companyList").empty();
            for(var i=0;i<data.length;i++)
            {
                $("#companyList").append("<option value='" + 
                data[i].CompanyName + "'></option>");
            }
        };
        $.ajax(options);
    });

});

The above jQuery code wires the input event handler for the companyName textbox. The code makes an Ajax call to an MVC action method - GetCompanyList(). This action method returns a list of CompanyName values from Customers table of the Northwind database. While making the Ajax call you pass the textbox value to the action method as the search criteria. The success function receives an array of JavaScript objects. Each object has a single property - CompanyName - that is then filled in <option> elements of companyList.

The GetCompanyList() action method finds all the company names containing the entered text. The GetCompanyList() action is shown below:

public JsonResult GetCompanyList(string criteria)
{
    NorthwindEntities db = new NorthwindEntities();
    var query = (from c in db.Customers
                    where c.CompanyName.Contains(criteria)
                    orderby c.CompanyName ascending
                    select new { c.CompanyName }).Distinct();
    return Json(query.ToList(),JsonRequestBehavior.AllowGet);
}

Notice that GetCompanyList() action returns JsonResult using Json() method. Also notice that JsonRequestBehavior is set to AllowGet so that GET requests can call this method.

That's it! You can now run the application and test whether it dynamically displays the autocomplete. The following figure shows a sample run.

image

READ MORE

Sometimes you need to display DropDownLists in your ASP.NET MVC views such that values in one DropDownList are dependent on the value selected in another DropDownList. The most common example of such a functionality is countries and states DropDownLists where based on a selected country you need to populate the states DropDownList. This article shows how such a cascading DropDownLists can be developed using ASP.NET MVC and jQuery.

Have a look at the following figure that shows two DropDownLists:

image

As you can see the country DropDownList contains a list of countries along with the first entry of "Please select". Upon selecting a country the states DropDownList displays the states belonging to the selected country. When the page is loaded the country DropDownList has "Please select" entry selected and states DropDownList is disabled. Upon selecting a country the states DropDownList is enabled so that state selection can be made. Clicking on the Submit button submits the form to an action method for further processing.

Begin by creating a new ASP.NET MVC4 project based on empty project template. Add Scripts folder to the project and place jQuery library into it. Then add HomeController to the Controllers folder. In a real world scenario you will get countries and states from a database. Here, for the sake of simplicity, we will use some hardcoded country and state values.

Now add the following action method to the HomeController:

public ActionResult Index()
{
  List<string> items = new List<string>();
  items.Add("Please select");
  items.Add("USA");
  items.Add("UK");
  items.Add("India");
  SelectList countries = new SelectList(items);
  ViewData["countries"] = countries;
  return View();
}

The above code shows Index() action method. Inside the Index() method a generic List of strings is created to hold country names and a few countries are added to it. The DropDownList HTML helper of ASP.NET MVC requires its data in the form of SelectList object. Hence a SelectList is created based on the countries List. The SelectList object is passed to the view using countries ViewData variable.

Next, add another action method to the HomeController as shown below:

public JsonResult GetStates(string country)
{
  List<string> states = new List<string>();
  switch (country)
  {
    case "USA":
      states.Add("California");
      states.Add("Florida");
      states.Add("Ohio");
      break;
    case "UK":
      //add UK states here
      break;
    case "India":
      //add India states hete
      break;
  }
  return Json(states);
}

As you can see the GetStates() action method accepts a string parameter named country and returns JsonResult. The GetStates() returns JsonResult because this method will be called using jQuery and states information is to be returned as JSON data. The GetStates() method contains a simple switch statement that adds a few states to states List based on the country value. Finally, the states generic List is returned to the caller using Json() method. The Json() method converts any .NET object into its JSON equivalent.

Now, add Index view to the Views folder and key-in the following markup to it:

<% using(Html.BeginForm("ProcessForm","Home",FormMethod.Post)){ %>
<div>Select Country :</div>
<%= Html.DropDownList("country", ViewData["countries"] as SelectList)%>
<br /><br />
<div>Select States :</div>
<select id="state"></select>
<br /><br />
<input type="submit" value="Submit" />
<%} %>

The Index view consists of a <form> that houses two DropDownLists. The country DropDownList is rendered using DropDownList HTML helper. The first parameter of the DropDownList() helper is the name of the DropDownList and the second parameter is the SelectList object containing DropDownList values. The second DropDownList is added as raw <select> element whose ID is state. Although the <form> is submitted to ProcessForm action this method is not described below as it's not directly related to the functioning of the DropDownLists.

Now, add a <script> reference to jQuery library and also add a <script> block in the head section of the view. Then write the following jQuery code in the <script> block:

$(document).ready(function () {
  $("#state").prop("disabled", true);
  $("#country").change(function () {
    if ($("#country").val() != "Please select") {
       var options = {};
       options.url = "/home/getstates";
       options.type = "POST";
       options.data = JSON.stringify({ country: $("#country").val() });
       options.dataType = "json";
       options.contentType = "application/json";
       options.success = function (states) {
       $("#state").empty();
       for (var i = 0; i < states.length; i++) {
         $("#state").append("<option>" + states[i] + "</option>");
       }
       $("#state").prop("disabled", false);
    };
    options.error = function () { alert("Error retrieving states!"); };
    $.ajax(options);
  }
  else {
    $("#state").empty();
    $("#state").prop("disabled", true);
  }
 });
});

The above code shows the ready() handler. Inside the ready() handler, you first disable the state DropDownList using prop() method. The prop() method sets the disabled DOM property to true and thus disables the state DropDownList. Then the change() method is used to wire change event handler of the country DropDownList. The change event handler will be called whenever selection in the country DropDownList changes. The change handler function first checks value selected in the country DropDownList. If it is other than "Please select", the code creates an options object. The options object holds various settings for the Ajax request to made to the server for retrieving the state values. The url property points to the GetStates() action method. The type property is set to POST indicating that a POST method will be used while making Ajax request. The data property contains JSON representation of the country selected in the country DropDownList. Note that the name of this property has to match with the name of the GetStates() method parameter. The dataType and contentType are set to json and application/json respectively. These properties indicate the data type of the response and request respectively.

The success handler function is called when the Ajax call to GetStates() is successful. The success handler function receives the states returned from the GetStates() method as an array of JSON objects. Inside the success handler you iterate through the states array and add <option> elements to the state DropDownList using append() method. Before appending the newly fetched states the state DropDownList is emptied. Once t he states are populated the disabled property of the states DropDownList is set to true using prop() method.

The error handler function simply displays an error message in an alert dialog.

Finally, an Ajax call is made using $.ajax() method of jQuery and the options object is passed as its parameter.

That's it! Run the application and test whether states are populated as expected.

READ MORE

Most of the times ASP.NET MVC views are rendered as a result of user navigating to some action. For example, when a user navigates to /home/index in the browser (either through address bar or through a hyperlink), ASP.NET MVC executes the action method and usually returns a view to the browser. This means each view is rendered as a result of a full GET or POST request. At times, however, you may want to load views dynamically through Ajax. This way you can render contents of a view without full page refresh.

Consider the following page:

image

The above page consists of a table that lists customers from the Customers table of Northwind database. Each customer row has two buttons - Customer Details and Order Details. Clicking on the respective button should display customer details and order details from the database. Without Ajax you would have submitted the page back to the server and then returned a view with the corresponding details. Using Ajax you can display the details without causing any postback to the server. This is shown below:

image

As you can see the above figure shows order details for CustomerID ALFKI above the customers table. These details are fetched via Ajax request.

While displaying data through Ajax request you have two options:

  • Fetch raw data from the server and embed it in HTML markup on the client side
  • Fetch HTML markup with data embedded from the server

Although the choice of the approach depends on a situation, it can be said that the former approach is suitable to make calls to Web API or when HTML display is dynamically decided by the client script. The later approach is suitable when ASP.NET MVC strongly typed views (or partial views) are being used to render the UI. In this example we will be using the later approach.

To develop this example, create a new ASP.NET MVC application based on the Empty template. Then add ADO.NET Entity Data Model for Customers and Orders tables of Northwind database. The Customer and Order entities are shown below:

image

Next, add HomeController and write the Index() action method as shown below:

public ActionResult Index()
{
    using (NorthwindEntities db = new NorthwindEntities())
    {
        List<Customer> model = db.Customers.ToList();
        return View(model);
    }
}

The Index() action simply retrieves all the Customer entities from the Customers DbSet and passes them to the Index view.

Now, add another action method - GetView() - to the HomeController as shown below:

public ActionResult GetView(string customerID,string viewName)
{
    object model = null;
    if(viewName=="CustomerDetails")
    {
        using(NorthwindEntities db=new NorthwindEntities())
        {
            model = db.Customers.Find(customerID);
        }
    }
    if (viewName == "OrderDetails")
    {
        using (NorthwindEntities db = new NorthwindEntities())
        {
            model = db.Orders.Where(o => o.CustomerID == customerID)
                      .OrderBy(o => o.OrderID).ToList();
        }
    }
    return PartialView(viewName,model);
}

The GetView() action method accepts two parameters - customerID and viewName. These two parameters are passed through an Ajax request. Depending on the viewName parameter either CustomerDetails partial view is returned to the caller or OrderDetails partial view is returned. These two view need model in the form of a Customer object and a List of Order entities respectively. That's why model variable is declared as object. Once model variable is populated the partial view name and the model is passed to the PartialView() method. Here, we used partial views because the HTML output is to be inserted in an existing page through Ajax.

Next, add one view (Index.cshtml) and two partial views (CustomerDetails.cshtml and OrderDetails.cshtml) to the Home sub-folder of Views folder.

Add the following markup to the CustomerDetails.cshtml partial view:

@model MVCViewsThroughAjax.Models.Customer

<table border="1" cellpadding="10">
    <tr>
        <td>Customer ID :</td>
        <td>@Model.CustomerID</td>
    </tr>
    <tr>
        <td>Company Name :</td>
        <td>@Model.CompanyName</td>
    </tr>
    <tr>
        <td>Contact Name :</td>
        <td>@Model.ContactName</td>
    </tr>
    <tr>
        <td>Country :</td>
        <td>@Model.Country</td>
    </tr>
</table>

The above markup is quite straightforward. The CustomerDetails partial view simply displays CustomerID, CompanyName, ContactName and Country of a Customer in a table.

Now add the following markup to the OrderDetails.cshtml partial page:

@model List<MVCViewsThroughAjax.Models.Order>

<table border="1" cellpadding="10">
    <tr>
        <th>Order ID</th>
        <th>Order Date</th>
        <th>Shipping Date</th>
        <th>Shipped To</th>
    </tr>
    @foreach(var item in Model)
    { 
        <tr>
            <td>@item.OrderID</td>
            <td>@item.OrderDate</td>
            <td>@item.ShippedDate</td>
            <td>@item.ShipCountry</td>
        </tr>
    }
</table>

The above markup iterates through the List of Order entities and renders a table with four columns - OrderID, OrderDate, ShippedDate and ShipCountry.

Now, add the following markup to the Index view:

@model List<MVCViewsThroughAjax.Models.Customer>

...
<html>
<head>
...
</head>
<body>
    <div id="viewPlaceHolder"></div>
    <br /><br />
    <table border="1" cellpadding="10">
        <tr>
            <th>Customer ID</th>
            <th>Company Name</th>
            <th colspan="2">Actions</th>
        </tr>
        @foreach(var item in Model)
        {
            <tr>
                <td>@item.CustomerID</td>
                <td>@item.CompanyName</td>
                <td><input type="button" class="customerDetails" 
                           value="Customer Details" /></td>
                <td><input type="button" class="orderDetails" 
                           value="Order Details" /></td>
            </tr>
        }
    </table>
</body>
</html>

The Index view receives a List of Customer entities as its model and renders a table with CustomerID, CompanyName and two buttons - Customer Details and Order Details.

Now comes the important part - making Ajax calls to display customer details and order details. Noticed the <div> at the beginning of the body section? The viewPlaceHolder is where the output of CustomerDetails.cshtml and OrderDetails.cshtml will be loaded. To do so we will use load() method of jQuery. Here is how that can be done:

$(document).ready(function () {

    $(".customerDetails").click(function (evt) {
        var cell=$(evt.target).closest("tr").children().first();
        var custID=cell.text();
        $("#viewPlaceHolder").load("/home/getview", 
            { customerID: custID, viewName: "CustomerDetails" });
    });

    $(".orderDetails").click(function (evt) {
        var cell = $(evt.target).closest("tr").children().first();
        var custID = cell.text();
        $("#viewPlaceHolder").load("/home/getview", 
           { customerID: custID, viewName: "OrderDetails" });
    });
});

Recollect that Customer Details and Order Details buttons have assigned CSS class of customerDetails and orderDetails respectively. The above jQuery code uses class selector to wire click event handlers to the respective buttons. Inside the click event handler of Customer Details button, the code retrieves the CustomerID from the table row. This is done using closest(), children() and first() methods. The CustomerID is stored in custID variable. Then load() method is called on viewPlaceHolder <div>. The first parameter of the load() method is the URL that will be requested through an Ajax request. The second parameter is a JavaScript object that supplies the data needed by the requested URL. In our example, GetView() action method needs two parameters - customerID and viewName. Hence the object has customerID and viewName properties. The customerID property is set to custID variable and viewName is set to CustomerDetails.

The click event handler of Order Details is similar but loads OrderDetails partial view.

That's it! You can now run the application and try clicking on both the buttons. The following figure shows customer details loaded successfully.

image

Notice that through out the application run the URL shown in the browser address bar remains unchanged indicating that Ajax requests are being made to display customer details and order details.

In the above example Ajax requests were made to /home/getview action. A user can also enter this URL in the browser's address bar producing undesirable results. As a precaution you can check the customerID and viewName parameters inside the GetView() action method (not shown in the above code). If these parameters are empty or contain invalid values you can throw an exception.

READ MORE

A common way to perform list, insert, update and delete operations in ASP.NET MVC is to create four separate views. The List view forms the launching view where records are displayed and you can choose to Edit, Delete or Insert a record. However, in some cases you may want to perform all these operations in a single view itself. This task can be accomplished using full page postback or using Ajax. This article discusses the former technique.

Consider the following figure that shows one such arrangement:

image

The above figure shows a list of records from Customers table of Northwind database. You can Insert a new customer by clicking on Insert button. You can select a row for editing by clicking on the Select button. The selected customer is shown below the main table for editing. Similarly you can also delete a customer by clicking on the Delete button.

Model and View Model

Let's see how the above application can be built. Begin by creating a new empty ASP.NET MVCproject in Visual Studio. Then add an ADO.NET Entity Data Model for the Customers table. The Customer entity class is shown below:

image

Then add a new POCO to the Models folder and name it CustomersViewModel. As you will see later, this view model class will be passed from the HomeController to the Index view. The CustomersViewModel class is shown below:

public class CustomersViewModel
{
    public List<Customer> Customers { get; set; }
    public Customer SelectedCustomer { get; set; }
    public string DisplayMode { get; set; }
}

The CustomersViewModel class consists of three properties. The Customers property holds a List of Customer that are to be displayed on the view. The SelectedCustomer property points to a Customer that is selected by the user. If no Customer is selected this property is null. The DisplayMode property indicates the mode of the Customer details area. Possible values are ReadOnly (after selection), ReadWrite (during edit) and WriteOnly (during insert). For the sake of simplicity DisplayMode is created as a string property, you can easily make it to accept an enumeration.

Home controller and its action methods

Then add HomeController in the Controllers folder. The HomeController will contain the following action methods:

  • Index()
  • Select()
  • New()
  • Insert()
  • Edit()
  • Update()
  • Delete()
  • Cancel()

The method names are self-explanatory. All the actions except Index() are called as a result of POST operation. Let's discuss them briefly one by one.

public ActionResult Index()
{
    using (NorthwindEntities db = new NorthwindEntities())
    {
        CustomersViewModel model = new CustomersViewModel();
        model.Customers = db.Customers.OrderBy(
                m => m.CustomerID).Take(5).ToList(); 
        model.SelectedCustomer = null;
        return View(model);
    }
}

The Index() action fetches a list of customers and fills it in the Customers view model property. The SelectedCustomer is set to null because there is no selected customer in the beginning. Note that for the sake of simplicity the above code fetches only 5 customers. You can, of course, fetch all if you so wish.

[HttpPost]
public ActionResult New()
{
    using (NorthwindEntities db = new NorthwindEntities())
    {
        CustomersViewModel model = new CustomersViewModel();
        model.Customers = db.Customers.OrderBy(
                       m => m.CustomerID).Take(5).ToList();
        model.SelectedCustomer = null;
        model.DisplayMode = "WriteOnly";
        return View("Index", model);
    }
}

The New() action is called when a user hits the Insert button at the top of the page. It fills the Customers list as before. SelectedCustomer is set to null because a new record is to be added. The DisplayMode is set to WriteOnly because we will be accepting new customer details. The following figure shows how the insert area looks like:

image

[HttpPost]
public ActionResult Insert(Customer obj)
{
    using (NorthwindEntities db = new NorthwindEntities())
    {
        db.Customers.Add(obj);
        db.SaveChanges();

        CustomersViewModel model = new CustomersViewModel();
        model.Customers = db.Customers.OrderBy(
                         m => m.CustomerID).Take(5).ToList();
        model.SelectedCustomer = db.Customers.Find(obj.CustomerID);
        model.DisplayMode = "ReadOnly";
        return View("Index", model);
    }
}

The Insert() action is called when a user fills new customer details and clicks on the Save button (see above figure). It receives a Customer object as its parameter. Inside, the Insert() action adds that new Customer to the database. It also sets the currently selected customer to the newly added customer by setting the SelectedCustomer property. The DisplayMode is set to ReadOnly so that the record is displayed in read-only manner.

[HttpPost]
public ActionResult Select(string id)
{
    using (NorthwindEntities db = new NorthwindEntities())
    {
        CustomersViewModel model = new CustomersViewModel();
        model.Customers = db.Customers.OrderBy(
                    m => m.CustomerID).Take(5).ToList();
        model.SelectedCustomer = db.Customers.Find(id);
        model.DisplayMode = "ReadOnly";
        return View("Index",model);
    }
}

The Select() action method is called when the Select button from a customer table row is clicked. It receives CustomerID as its parameter. Inside, it fills Customers list as before. This time SelectedCustomer property is set to the Customer whose CustomerID is passed. The DisplayMode property is set to ReadOnly to indicate that the details of the selected customer should be displayed in a read-only table below the main customer listing (see below).

image

[HttpPost]
public ActionResult Edit(string id)
{
    using (NorthwindEntities db = new NorthwindEntities())
    {
        CustomersViewModel model = new CustomersViewModel();
        model.Customers = db.Customers.OrderBy(
                        m => m.CustomerID).Take(5).ToList();
        model.SelectedCustomer = db.Customers.Find(id);
        model.DisplayMode = "ReadWrite";
        return View("Index", model);
    }
}

The Edit() action is called when a user clicks on the Edit button once a Customer is selected. Inside, it sets the SelectedCustomer property to the Customer whose CustomerID is passed to the method. DisplayMode property is set to ReadOnly to display that record in editable table as shown below:

image

[HttpPost]
public ActionResult Update(Customer obj)
{
    using (NorthwindEntities db = new NorthwindEntities())
    {
        Customer existing = db.Customers.Find(obj.CustomerID);
        existing.CompanyName = obj.CompanyName;
        existing.ContactName = obj.ContactName;
        existing.Country = obj.Country;
        db.SaveChanges();
                
        CustomersViewModel model = new CustomersViewModel();
        model.Customers = db.Customers.OrderBy(
                      m => m.CustomerID).Take(5).ToList();

        model.SelectedCustomer = existing;
        model.DisplayMode = "ReadOnly";
        return View("Index", model);
    }
}

The Update() action is called when a user modifies an existing Customer data and clicks on the Save button (see above figure). Inside, the code updates an existing Customer and saves the changes back to the database. Then Customers, SelectedCustomer and DisplayMode properties of the view model are set.

[HttpPost]
public ActionResult Delete(string id)
{
    using (NorthwindEntities db = new NorthwindEntities())
    {
        Customer existing = db.Customers.Find(id);
        db.Customers.Remove(existing);
        db.SaveChanges();

        CustomersViewModel model = new CustomersViewModel();
        model.Customers = db.Customers.OrderBy(
                          m => m.CustomerID).Take(5).ToList();

        model.SelectedCustomer = null;
        model.DisplayMode = "";
        return View("Index", model);
    }
}

The Delete() action is called when Delete button in any of the customer row is clicked. It receives CustomerID as its parameter. Inside, it removes the specified Customer and saves the changes back to the database. The SelectedCustomer is set to null because post deletion that customer no longer exists in the database. For the same reason, DisplayMode is set to an empty string.

[HttpPost]
public ActionResult Cancel(string id)
{
    using (NorthwindEntities db = new NorthwindEntities())
    {
        CustomersViewModel model = new CustomersViewModel();
        model.Customers = db.Customers.OrderBy(
                          m => m.CustomerID).Take(5).ToList();
        model.SelectedCustomer = db.Customers.Find(id);
        model.DisplayMode = "ReadOnly";
        return View("Index", model);
    }
}

The Cancel() action is called when Cancel button from the Edit area is clicked. It receives CustomerID as its parameter. It changes the DisplayMode from ReadWrite to ReadOnly so that the SelectedCustomer is displayed in read-only fashion.

Notice that all the above action methods return Index view and CustomerViewModel object.

Index view

Now, let's see what goes inside the Index view.

@model MasterDetailsDemo.Models.CustomersViewModel

@{
    Layout = null;
}

<!DOCTYPE html>

<html>
<head>
    <meta name="viewport" content="width=device-width" />
    <title>Index</title>
    <style>
        .SelectedCustomer
        {
            background-color:gray;
            font-weight:bold;
        }
    </style>
</head>
<body>
    <h1>List of Customers</h1>

    <form method="post">
        <input type="submit" 
         value="Insert" formaction="/home/new" />
        <br /><br />
        <table border="1" cellpadding="10">
            <tr>
                <th>CustomerID</th>
                <th>CompanyName</th>
                <th colspan="2">Actions</th>
            </tr>
            @foreach (var item in Model.Customers)
            {
                if (Model.SelectedCustomer != null)
                {
                    if (item.CustomerID == 
                        Model.SelectedCustomer.CustomerID)
                    {
                        @:<tr class="SelectedCustomer">
                    }
                    else
                    {
                        @:<tr>
                    }
                }
                else
                {
                    @:<tr>
                }
                <td>@item.CustomerID</td>
                <td>@item.CompanyName</td>
                <td><input type="submit" 
                     formaction="/home/select/@item.CustomerID" 
                     value="Select" /></td>
                <td><input type="submit" 
                     formaction="/home/delete/@item.CustomerID" 
                     value="Delete" /></td>
                @:</tr>
            }
        </table>
    </form>
    <br /><br />
    @{
        if(Model.SelectedCustomer!=null)
        {
            if (Model.DisplayMode == "ReadOnly")
            {
                Html.RenderPartial
                ("ShowCustomer",Model.SelectedCustomer);
            }
            if (Model.DisplayMode == "ReadWrite")
            {
                Html.RenderPartial
                ("EditCustomer",Model.SelectedCustomer);
            }
        }
        if (Model.DisplayMode == "WriteOnly")
        {
            Html.RenderPartial("InsertCustomer",
            new MasterDetailsDemo.Models.Customer());
        }
    }
</body>
</html>

The Index view is divided into two logical parts. The top part displays a list of customers in a table. Notice that a CSS class SelectedCustomer is applied to the row that contains the selected CustomerID. The bottom part displays a Partial Page based on the value of DisplayMode. This way either show, insert or edit areas are displayed. Notice that there are three partial pages involved:

  • ShowCustomer.cshtml
  • EditCustomer.cshtml
  • InsertCustomer.cshtml

These three partial pages render the read-only, read-write and write-only displays respectively. All of them take Customer object as their model. Let's see each of these partial pages one by one.

ShowCustomer partial page

The following code shows ShowCustomer.cshtml partial page.

@model MasterDetailsDemo.Models.Customer

@using(Html.BeginForm("Edit","Home",FormMethod.Post))
{ 
<table border="1" cellpadding="10">
    <tr>
        <td>Customer ID :</td>
        <td>@Model.CustomerID</td>
    </tr>
    <tr>
        <td>Company Name :</td>
        <td>@Model.CompanyName</td>
    </tr>
    <tr>
        <td>Contact Name :</td>
        <td>@Model.ContactName</td>
    </tr>
    <tr>
        <td>Country :</td>
        <td>@Model.Country</td>
    </tr>
    <tr>
        <td colspan="2">
            <input type="submit" value="Edit" 
                   formaction="/home/edit/@Model.CustomerID" />
            <input type="submit" value="Cancel" 
                   formaction="/home/index" />
        </td>
    </tr>
</table>
}

Notice that the Edit and Cancel buttons submit to /home/edit and /home/index respectively. The other markup from the partial page is quite straightforward and displays CustomerID, CompanyName, ContactName and Country columns for a selected customer.

EditCustomer partial page

The following code shows what goes inside EditCustomer.cshtml:

@model MasterDetailsDemo.Models.Customer

@using (Html.BeginForm("Update", "Home", FormMethod.Post))
{
    <table border="1" cellpadding="10">
        <tr>
            <td>Customer ID :</td>
            <td>@Html.TextBoxFor(m => m.CustomerID, 
                         new { @readonly = "readonly" })</td>
        </tr>
        <tr>
            <td>Company Name :</td>
            <td>@Html.TextBoxFor(m => m.CompanyName)</td>
        </tr>
        <tr>
            <td>Contact Name :</td>
            <td>@Html.TextBoxFor(m => m.ContactName)</td>
        </tr>
        <tr>
            <td>Country :</td>
            <td>@Html.TextBoxFor(m => m.Country)</td>
        </tr>
        <tr>
            <td colspan="2">
                <input type="submit" value="Save" 
                       formaction="/home/update" />
                <input type="submit" value="Cancel" 
                       formaction="/home/cancel/@Model.CustomerID" />
            </td>
        </tr>
    </table>
}

Note that Save button and Cancel button submit to /home/update and /home/cancel respectively.

InsertCustomer partial page

Finally, here is the markup of InsertCustomer.cshtml:

@model MasterDetailsDemo.Models.Customer

@using (Html.BeginForm("Insert", "Home", FormMethod.Post))
{
    <table border="1" cellpadding="10">
        <tr>
            <td>Customer ID :</td>
            <td>@Html.TextBoxFor(m => m.CustomerID)</td>
        </tr>
        <tr>
            <td>Company Name :</td>
            <td>@Html.TextBoxFor(m => m.CompanyName)</td>
        </tr>
        <tr>
            <td>Contact Name :</td>
            <td>@Html.TextBoxFor(m => m.ContactName)</td>
        </tr>
        <tr>
            <td>Country :</td>
            <td>@Html.TextBoxFor(m => m.Country)</td>
        </tr>
        <tr>
            <td colspan="2">
                <input type="submit" value="Save" 
                       formaction="/home/insert" />
                <input type="submit" value="Cancel" 
                       formaction="/home/index" />
            </td>
        </tr>
    </table>
}

The Save and Cancel button submit to /home/insert and /home/index respectively.

That's it! All the parts of the application are in place. Run the application and test whether all the operations work as expected.

READ MORE

Recently during a training program one of the participant asked this question - "How to create a login page using jQuery Ajax in MVC applications?" This article is illustrates how Ajax login can be implemented using Forms authentication, Membership and jQuery $.ajax().

Implementing Ajax based login involves many of the same steps as the normal forms authentication. However, the login page doesn't send user ID and password to the server through a standard form submission. Instead, user credentials are sent to the server via an Ajax request. The credentials are then validated on the server and the result of the verification process is conveyed to the client. If the login attempt was successful, the user is taken to the secured area of the website.

Let's understand how all this works by developing a sample application. Begin by creating a new ASP.NET MVC Web Application using an empty template. To keep things simple we will add only those things to the project that are absolutely essential to the functioning of this example.

Configure a database for membership services

First of all you need to configure a database for membership services. This is done with the help of aspnet_regsql.exe tool. Open Visual Studio developer command prompt and issue the said command to open the configuration wizard. Simply follow the wizard to configure your database. For example, here I am configuring Northwind database to support membership services.

image

Configure forms authentication and membership provider

Next, open the web.config of the web application and configure the authentication scheme as shown below:

<connectionStrings>
  <add name="connstr" connectionString="data source=.\sqlexpress;
                      initial catalog=Northwind;integrated security=true"/>
</connectionStrings>

<system.web>
  <authentication mode="Forms">
    <forms loginUrl="~/account/login" defaultUrl="~/home/index"></forms>
  </authentication>
  ...
</system.web>

The <authentication> tag sets the authentication mode to Forms. The forms authentication is configured to have login page as ~/account/login and default page as ~/home/index. The <connectionStrings> section defines a database connection string for the Northwind database. This connection string is used while configuring the membership provider.

To configure the membership provider add the following markup to the web.config file:

<membership defaultProvider="p1">
  <providers>
    <add name="p1" connectionStringName="connstr" 
                   type="System.Web.Security.SqlMembershipProvider" />
  </providers>
</membership>

Create Account controller

Then add a controller to the Controllers folder - AccountController. The Account controller contains code that validates a user. The Login() and ValidateUser() action methods of the Account controller are shown below:

public ActionResult Login()
{
    return View();
}

[HttpPost]
public JsonResult ValidateUser(string userid, string password, 
                               bool rememberme)
{
    LoginStatus status = new LoginStatus();
    if (Membership.ValidateUser(userid, password))
    {
        FormsAuthentication.SetAuthCookie(userid, rememberme);
        status.Success = true;
        status.TargetURL = FormsAuthentication.
                           GetRedirectUrl(userid, rememberme);
        if (string.IsNullOrEmpty(status.TargetURL))
        {
            status.TargetURL = FormsAuthentication.DefaultUrl;
        }
        status.Message = "Login attempt successful!";
    }
    else
    {
        status.Success = false;
        status.Message = "Invalid UserID or Password!";
        status.TargetURL = FormsAuthentication.LoginUrl;
    }
    return Json(status);
}

The Login() action method simply returns the Login view. The ValidateUser() method is important to us because this method validates the user credentials and is called via Ajax. The ValidateUser() method takes three parameters - userid, password and rememberme. Inside, it calls ValidateUser() method of the Membership object to decide whether the user ID and password is correct. The ValidateUser() method also creates an instance of LoginStatus class - a POCO that is intended to store the status of the login process. The LoginStatus class looks like this:

public class LoginStatus
{
    public bool Success { get; set; }
    public string Message { get; set; }
    public string TargetURL { get; set; }
}

The LoginStatus class consists of three properties - Success, Message and TargetURL. The Success boolean property holds true if the login attempt was successful, false otherwise. The Message property holds a succcess or error message that is to be displayed to the end user. The TargetURL property holds the page URL where the user should be redirected if the login attempt was successful.

Coming back to the ValidateUser() method, if the user credentials are valid a forms authentication cookie is issued using the SetAuthCookie() method. The LoginStatus object is populated with the required information. Notice how the TargetURL is determined using GetRedirectUrl() method and DefaultUrl properties of the FormsAuthentication class.

If the login attempt was unsuccessful, LoginStatus object is populated with error information. Finally, LoginStatus object is sent back to the caller using Json() method. Remember that ValidateUser() method will be called using Ajax and hence should return data to the browser in JSON format.

Create Login view

Now add the Login view and design it as shown below:

image

The Login view consists of a textbox, a password box, a checkbox and a button. Clicking on the Login button initiates an Ajax request to the ValidateUser() method you created earlier. The jQuery code responsible for calling the ValidateUser() method is given below:

$(document).ready(function () {
    $("#login").click(function () {
        $("#message").html("Logging in...");
        var data = { "userid": $("#userid").val(), 
                     "password": $("#password").val(), 
                     "rememberme":$("#rememberme").prop("checked") };
        $.ajax({
            url: "/account/validateuser",
            type: "POST",
            data: JSON.stringify(data),
            dataType: "json",
            contentType: "application/json",
            success: function (status) {
                $("#message").html(status.Message);
                if (status.Success)
                {
                    window.location.href = status.TargetURL;
                }
            },
            error: function () {
                $("#message").html("Error while authenticating 
                                    user credentials!");
            }
        });
    });
});

Observe this code carefully. Upon clicking the login button a progress message is displayed in a message <div> element. The code then forms a JavaScript object that has three properties - userid, password and rememberme. These property names must match the parameter names of the ValidateUser() action method you created earlier. Then $.ajax() of jQuery is used to make an Ajax request to /account/validateuser. The type of the request is set to POST. The data setting contains the stringified version of the data JavaScript object you just created. The dataType and contentType properties are set to json and application/json respectively. These two properties represent the response format and the request MIME content type respectively. The success function receives a status object. This object is a JSON representation of LoginStatus object you return from the ValidateUser() method. If the Success property is true it means that the login attempt was successful and the user is redirected to the TargetURL using window.location.href property. If the login attempt fails an error message is displayed in the message <div>. The error function is invoked if there is any error making the Ajax call and simply displays an error message to the user.

The following figure shows the login view in action:

image

Create Home controller and Index view

If a login attempt is successful the use is taken to the Index view. So, add the Home controller and also the Index view. The Home controller is supposed to be a secured one and hence add [Authorize] attribute on top of the Index() action method or the HomeController class.

[Authorize]
public class HomeController : Controller
{
    public ActionResult Index()
    {
        return View();
    }
}

The Index view simply outputs a welcome message:

<body>
<h1>Welcome @Membership.GetUser().UserName!</h1>
</body>

The following figure shows a successful run of the Index view:

image

 

To test the application you just developed you need to create a new user account. You can do so either by creating a registration page or by adding a few test users in the Global.asax. For the sake of this example the later approach is alright. Here is how you can create a new user.

protected void Application_Start()
{
  ...
  MembershipCreateStatus status;
  Membership.CreateUser("User1", 
  "some_password_here", "user1@somewebsite.com", 
  "question", "answer", true, out status);
}

That's it! The Ajax login for your MVC application is ready :-)

READ MORE
...