Another JQuery Autocomplete Example With ASP.NET Webforms

This is a quick run through of how to implement a jQuery autocomplete with webforms. The lookup data consists of about 600 items and changes very rarely so I want to load the data only once when the page loads rather than sending a query to the database as the user types in the input box.

On the Webform is a input box which will have autocomplete feature.

<div>
    <asp:Label ID="Label5" runat="server" Text="Depot Code:" AssociatedControlID="txtDepotCode" />
    <asp:TextBox ID="txtDepotCode" runat="server" ClientIDMode="Static" TabIndex="5" />
    <asp:TextBox ID="txtDepotName" runat="server" ClientIDMode="Static" TabIndex="6" />
</div>

In this javascript code a webservice is being called to retrieve the items via an ajax call. When the data is returned it is set as the source data for the autocomplete input box.

$(function () {
    $.ajax({
        type: "POST",
        contentType: "application/json; charset=utf-8",
        url: "creditqueryservice.asmx/GetDepots",
        dataType: "json"
    }).done(function (response) {
        var data = $.map(response.d, function (item) {
            return {
                value: item.DepotCode,
                label: item.DepotCode + ' - ' + item.DepotName
            };
        });

        $("#txtDepotCode").autocomplete({
            source: data,
            minLength: 2,
            select: function(event, ui) { 
                // do something here.  ui parameter holds the values of the item selected
                $("#" + this.id).val(ui.item.value);
                $("#" + this.id + "name").val(ui.item.DepotName);
                return false;
            }
        });
    });
});

The depots are retrieved via a repository (_branchesRepo) variable in the class. Retrieve the data suitable to your application.

[WebMethod]
public List<Depot> GetDepots()
{
    var depots = new List<Depot>();
     if (depots == null || depots.Count == 0)
    {
        depots = _branchesRepo.GetBranches();
    }
    return depots;
}

To validate what the user has entered the autocomplete data can retrieved as in the code below.

var source = $("#txtDepotCode").autocomplete("option", "source");

Now you can compare the value entered with the values in the list. You would really only do this if the user typed into the input box without selecting an item from the matched list.

Validate at least one option is selected in radio button list on Webform

On a webform there is a radiobutton list. The user must select at least one of the options for the form to be valid.

<div>
	<asp:Label ID="Label37" runat="server" Text="Is Sales Item:" AssociatedControlID="rblSaleItem" />
	<asp:RadioButtonList ID="rblSaleItem" runat="server" ClientIDMode="Static" TabIndex="36"
		RepeatDirection="Horizontal" RepeatLayout="Flow" CssClass="checkbox-list">
		<asp:ListItem Text="Yes" Value="1"></asp:ListItem>
		<asp:ListItem Text="No" Value="0"></asp:ListItem>
	</asp:RadioButtonList>
	<asp:CustomValidator ID="IsSaleItemCustomValidator" runat="server" Text="Is Sales Item: selection is required"
		Display="Dynamic" ClientIDMode="Static" ClientValidationFunction="IsSalesItemValidator"
		OnServerValidate="IsSalesItemValidation" ValidationGroup="FORMGROUP" CssClass="field-validation-error"></asp:CustomValidator>
</div>

Here we have the javascript validation. The radiobutton list and validator are in the same div element, so from the validation control triggered we go up to the parent to find the radiobutton list. Each radio button in the list is tested to see if it is selected.

// Javascript client side validation
function IsSalesItemValidator(sender, args) {
    args.IsValid = false;

    $("#" + sender.id).parent().find(".checkbox-list").find(":radio").each(function () {
        if ($(this).is(":checked")) {
            args.IsValid = true;
            return;
        }
    });
}

On the server side the validation works in a similar way.

// C# Server side validation
protected void IsSalesItemValidation(object source, ServerValidateEventArgs args)
{
    // has a sale item check box been selected
    foreach (ListItem li in rblSaleItem.Items)
    {
        if (li.Selected)
        {
            args.IsValid = true;
            break;
        }
    }
}

I actually do not like the idea of iterating through the radiobutton list to find if one is checked. Normally the radiobutton lists are quite short on a page so it is probably not a big overhead, but it is too old school lazy coding for my liking. In .Net3.5 the Enumerable class was introduced in the System.Linq namespace. Unsing the Enumerable.Cast method we can construct linq queries to retrieve the selected item from the radiobutton list instead of iterating all the items.

protected void IsSalesItemValidation(object source, ServerValidateEventArgs args)
{
    var selectedItem = (from li in rblSaleItem.Items.Cast<ListItem>()
                        where li.Selected == true
                        select li).FirstOrDefault();
    /*
    // Alternatively if you prefer inline linq
    var selectedItem = rblSaleItem.Items.Cast<ListItem>()
                        .Where(item => item.Selected == true).FirstOrDefault();
    */
    
    args.IsValid = SelectedItem != null && SelectedItem.Selected;
}

Using the FirstOrDefault() method will return null if there are not selected items in the radiobutton list. If we were to use the First() method an InvalidOperationException would be thrown if there are no elements that meet the query conditions.
It is safe to use the Cast method in this situation because it is guaranteed all the items in the collection are of the ListItem type. If the Cast method was used on a collection containing different objects an InvalidCastException will be thrown. If the collection object itself is null an ArgumentNullException will be thrown.

JQuery Autocomplete Example With ASP.NET Webforms

This will be a quick run through of how I implemented autocomplete in a Webforms application.

The data for the autocomplete list is represented in a class.

public class Depot
{
    public string DepotCode { get; set; }
    public string DepotName { get; set; }
}

A WebMethod in a service class is called to retrieve the list. I am keeping the amount of the data small for brevity. In this simple example I am checking what the search term starts with and populating a list with similar values. In a real situation the list being returned from this WebMethod would be generated from a search in a database.

[WebMethod]
public List<Depot> GetDepots(string search)
{
    if (search.StartsWith("11"))
    {
        return new List<Depot>{ 
            new Depot{DepotCode= "1111",DepotName= "London"},
            new Depot{DepotCode= "1122",DepotName= "Birmingham"}
        };
    }
    else
    {
        return new List<Depot>{ 
            new Depot{DepotCode= "0111",DepotName= "London"},
            new Depot{DepotCode= "0122",DepotName= "Birmingham"}
        };
    }
}

This javascript is the magic. The request parameter of the source function contains the search text, in this case the text I enter into the input box. I have set minLength to 2, so 2 characters must be entered for the autocomplete list to appear.

In the success function each item of data is transposed into an object consisting of a value and label property. The label property is the text displayed in the autocomplete list.  When an item in the list is selected the Value property is the text that populates the input box.

$("#txtDepotCode").autocomplete({
    source: function (request, response) {
        $.ajax({
            type: "POST",
            contentType: "application/json; charset=utf-8",
            url: "creditqueryservice.asmx/GetDepots",
            data: "{'search':'" + request.term + "'}",
            dataType: "json",
            success: function (data) {
                response($.map(data.d, function (item) {
                    return {
                        value: item.DepotCode,
                        label: item.DepotCode +' - '+ item.DepotName
                    };
                }));
            },
            error: function (result) {
                alert("Error");
            }
        });
    },
    minLength: 2
});

There could potentially be any number of issues but I got this issue

object doesn't support property or method 'curCSS'

which turned out to be a mismatch between the version of jQuery and the version of jQueryUI. After changing the version of jQueryUI this issue went away.

How to update partial view using jquery ajax

I wanted to give a quick but clear example how to use jQuery to update a partial view, an example which could be easy to replicate.

We need a model
The model consists of product class with three properties; a product code, name and price.

public class Product
{
    [Required]
    public string Code { get; set; }
    [Required]
    public string Name { get; set; }
    [Required]
    [DataType(DataType.Currency)]
    public decimal UnitPrice { get; set; }
}

And a Controller
For simplicity there is no specific data layer or repository, the controller contains a global array of products. When a http post is made to the GetProducts ActionResult a parameter is passed specifying how many products to return.

public class HomeController : Controller
{
    private List<Product> products = new List<Product>() ;

    public HomeController()
    {
        products.Add(new Product { Code = "P001", Name = "Hair Dryer", UnitPrice = 10.50M });
        products.Add(new Product { Code = "P002", Name = "Carpet Cleaner", UnitPrice = 3.60M });
        products.Add(new Product { Code = "P003", Name = "Hair Remover", UnitPrice = 12.10M });
        products.Add(new Product { Code = "P004", Name = "Vacuum Cleaner", UnitPrice = 80.99M });
        products.Add(new Product { Code = "P005", Name = "Table", UnitPrice = 7.69M });
        products.Add(new Product { Code = "P006", Name = "Duvet Cover", UnitPrice = 12.89M });
        products.Add(new Product { Code = "P007", Name = "Towel", UnitPrice = 3.50M });
        products.Add(new Product { Code = "P008", Name = "Electric Oven", UnitPrice = 125.99M });
        products.Add(new Product { Code = "P009", Name = "Hair Straighteners", UnitPrice = 8.90M });
        products.Add(new Product { Code = "P010", Name = "Belt", UnitPrice = 3.50M });
    }

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

    [HttpPost]
    public ActionResult GetProducts(int quantity)
    {
        var selectedProducts = products.Take(quantity);
        return PartialView("_ProductList", selectedProducts);
    }
}

The Main View
mvcpartialviewupdate-main-view
Selecting from the dropdown list causes a change event the initiates the post to the server. If the post is successful the partial view is updated.
The date and time is displayed for comparison with the date and time on the partial view, they will be different.

<h2>Product Display</h2>
<div class="col-sm-6">
    @DateTime.Now.ToLongDateString()
    @DateTime.Now.ToLongTimeString()
<div class="panel panel-default">
<div class="panel-heading">
            Products</div>
<div class="panel-body">
<div class="form-group form-group-sm">
                <label class="control-label" for="PageSize">
                    How many products should be displayed?
                </label>
<div>
                    <select id="PageSize">
<option value="1">1</option>
<option value="2">2</option>
<option value="3">3</option>
<option value="4">4</option>
<option value="5">5</option>
<option value="6">6</option>
<option value="7">7</option>
<option value="8">8</option>
<option value="9">9</option>
<option value="10">10</option>
</select></div>
</div>
<div id="SelectedProducts"></div>
</div>
</div>
</div>

jQuery AJAX
When a selection is made from the dropdown list the change event is triggered and a POST made to the server.

@section scripts{
    <script type="text/javascript">

        $(document).ready(function () {

            $("#PageSize").on("change", function () {
                var val = $('#PageSize').val();
                $.ajax({
                    url: "/Home/GetProducts",
                    type: "POST",
                    data: { quantity: val }
                })
                .done(function (partialView) {
                    $("#SelectedProducts").html(partialView);
                });
            });
        });
    </script>
}

And the Partial View (_ProductList.cshtml)
The partial view displays a list of products in a table. The POST date and time is displayed for comparison purposes.

@model IEnumerable<MVCUpdatePartial.Models.Product>
<div class="editor-label">
    Selected Products</div>
<table class="table">
    @foreach(var product in Model)
    {
<tr>
<td>@product.Code</td>
<td>@product.Name</td>
<td>@product.UnitPrice</td>
</tr>
}</table>
Updated: @DateTime.Now.ToLongDateString()
@DateTime.Now.ToLongTimeString()

After the POST
And how the view looks after the POST and the partial view is updated.
mvcpartialviewupdate-main-view-after-post

Simple single page web application using knockoutjs

Here I am going to demonstrate a simple (emphasis on simple) single-page application, using knockoutjs. For more information on knockoutjs visit their official website.

This demo site I will be going through is for requesting conference tickets and is written as an ASP.Net MVC (Razor) application. The layout page contains the script references to jquery, bootstrap and knockoutjs and contains the template header, menu and footer sections. I will not be going through the implementation of these libraries or layout page in detail.

The ticket request site has three views
1. Introduction (intro)
single page intro view
2. Ticket Request Form (requestform)
single page request view
3. Ticket Request Confirmation (confirmation)
single page confirmation view

There are limitations

  • The request form does not contain any validation.
  • The request does not actually get sent anywhere.

First of we’ll look at the script.

<script type="text/javascript" language="javascript">
    var model = {
        view: ko.observable("intro"),
        request: {
            name: ko.observable(""),
            email: ko.observable(""),
            phone: ko.observable(""),
            quantity: ko.observable(0)
        },
    }
    var showForm = function () {
        model.view("requestform");
    }
    var sendRequest = function () {
        model.view("confirmation")
    }

    var showIntro = function () {
        model.view("intro")
    }

    $(document).ready(function () {
        ko.applyBindings();
    })
</script>

The viewmodel has two properties, the view property is an observable string containing the the name of the current view being displayed.  The request property is an object that hold the details of the request and contains the following observable properties name, email, phone and quantity.

There are three methods named showIntro, showForm and sendRequest. These methods are called when
clicking the navigation buttons at the bottom of the views. Each method will set the observable view property of the viewmodel to the respective view name.
The model is bound to the page using the ko.applyBindings() call when the page has finished loading.

Now let’s look at the html code.

All the views are wrapped in a single form element. Each view is contained in a separate DIV element.  On each view notice how the visibility of the outer DIV is controlled by a condition comparing the view name with the view property of the viewmodel.
The introduction holds instructions for completing the request.  This view will only be visible when the viewmodel view property is set to ‘intro’.

<div data-bind="visible: model.view() == 'intro'">
    <div class="panel panel-success">
        <div class="panel-heading"><h4>Conference Tickets</h4></div>
        <div class="panel-body">
            <div class="text-justify">
                <p>
                    This is a demonstration of a simple single-page application using Knockoutjs.
                </p>
                <strong>There are three sections</strong>
                <ul class="list-unstyled">
                    <li><span class="glyphicon glyphicon-info-sign"></span> Introduction (This page)</li>
                    <li><span class="glyphicon glyphicon-tasks"></span> Request Form</li>
                    <li><span class="glyphicon glyphicon-ok"></span> Thank you/Confirmation</li>
                </ul>

                <strong>Notes:</strong>
                <ul class="list-unstyled">
                    <li><span class="glyphicon glyphicon-asterisk"></span>There is no validation on form fields or on form submission.</li>
                    <li><span class="glyphicon glyphicon-asterisk"></span>This is a coding example only, the request is not actually sent anywhere.</li>
                    <li><span class="glyphicon glyphicon-asterisk"></span>No personal information is collected or sent.</li>
                </ul>
            </div>
            <div class="text-center">
                <button class="btn btn-success" data-bind="click: showForm">Next</button>
            </div>
        </div>
    </div>
</div>

The request form gathers information relating to the conference ticket request.  This view will only be visible when the viewmodel view property is set to ‘requestform’.

<div data-bind="visible: model.view() == 'requestform'">
    <div class="panel panel-success">
        <div class="panel-heading"><h4>Conference Tickets</h4></div>
        <div class="panel-body">
            <div class="col-xs-4">
                <div class="form-group">
                    <label>Full name:</label>
                    <input type="text" class="form-control" data-bind="value: model.request.name" />
                </div>
                <div class="form-group">
                    <label>Contact email:</label>
                    <input class="form-control" data-bind="value: model.request.email" required />
                </div>
                <div class="form-group">
                    <label>Contact phone:</label>
                    <input class="form-control" data-bind="value: model.request.phone" />
                </div>

                <div class="form-group">
                    <label>How many will attend?</label>
                    <select class="form-control" data-bind="value: model.request.quantity">
                        <option value="0">Not Attending</option>
                        <option value="1">1</option>
                        <option value="2">2</option>
                        <option value="3">3</option>
                        <option value="4">4</option>
                    </select>
                </div>
            </div>

            <div class="col-xs-12 text-center">
                <button class="btn btn-success" data-bind="click: showIntro"> Back </button>
                <button class="btn btn-success" data-bind="click: sendRequest"> Submit Request </button>
            </div>
        </div>
    </div>
</div>

Finally, the confirmation view displays a summary of the request. This view will only be visible when the viewmodel view property is set to ‘confirmation’.

<div data-bind="visible: model.view() == 'confirmation'">
    <h1>
        <span data-bind="visible: model.request.quantity() == '0'">
            Oh no! <span data-bind="text: model.request.name()"></span>, I'm gutted you're not coming.
        </span>
        <span data-bind="visible: model.request.quantity() != '0'">
            Yay! <span data-bind="text: model.request.name()"></span> is coming.
        </span>
    </h1>
    <div class="lead">
        <strong>Here are the details submitted</strong>

        Contact phone: <span data-bind="text: model.request.email()"></span>

        Contact email: <span data-bind="text: model.request.phone()"></span>

        No of tickets: <span data-bind="text: model.request.quantity()"></span>

    </div>
    <div class="text-center">
        <button class="btn btn-success" data-bind="click: showForm">Back</button>
    </div>
</div>

As I often say, this is not the only way but it is effective. There can be as many views as are required.