Thursday, 2 June 2016

A worked example of autocomplete for a ASP.NET MVC application using jQuery

This post contains a worked example of a autocomplete text box for a ASP.NET MVC application along with some comments on the implementation.

The first step is to add the jQuery library for jQuery.UI.Combined by adding it to the project using Nuget

Once that has been added, ensure that the scripts are served up by adding this code to RegisterBundles(BundleCollection bundles) in  \App_Start\BundleConfig.cs

 bundles.Add(new ScriptBundle("~/bundles/jqueryui").Include("~/Scripts/jquery-ui-{version}.js"));

With the library included in the build, add a include for autocomplete.css and jqueryui into the .cshtml file where it is required. In my project I added it to _Layout.cshtml

<head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>@ViewBag.Title - Toy Library</title>
    @Styles.Render("~/Content/css")
    @Scripts.Render("~/bundles/modernizr")
    @Scripts.Render("~/bundles/jquery")
    @Scripts.Render("~/bundles/jqueryui")
    @Scripts.Render("~/bundles/bootstrap")
    @RenderSection("scripts", required: false)
    <link href="~/Content/themes/base/jquery-ui.css" rel="stylesheet" type="text/css" />
    <link href="~/Content/themes/base/autocomplete.css" rel="stylesheet" type="text/css" />
</head>


Next is to add a method to the controller that will return a Json list of the search terms you need. This example is a little longer that what you would normally need because the search string is made up of two properties of the model - ie the code below to gets two lists then concatenate them together to create the final list.

        //return a list of member names which contain the searchExpr
        //for use by the autocomplete search box for members data_autocomplete_source = Url.Action("QuickSearchMembers", "Members")
        public ActionResult QuickSearchMembers( string term )
        {
            List<Member> memberList = db.Members.Where(a => a.FirstName.Contains(term)).ToList();
            List<Member> memberListLast = db.Members.Where(b => b.LastName.Contains(term)).ToList();

            //merge together.
            if (memberList.Count > 0) { memberList.Concat(memberListLast); }
            else { memberList = (List<Member>)memberListLast; }
           
            //return only 5 items to reduce bandwidth
            if (memberList.Count > 5)
            {
                memberList.RemoveRange(5, memberList.Count - 5);
            }
            var memberNames = memberList.Select(b => new { value = b.MemberName });
            return Json(memberNames, JsonRequestBehavior.AllowGet);
        }


Another feature of this code is that the returned list is limited to 5 values. This is done to reduce the size of the Json packet sent on each request if there is the potential to have a large amount of records retruned (I have some more comments on this below).

In the .cshtml page where this will be used, add the following mark-up to display the search text box

@using (Html.BeginForm("Index", "Members", FormMethod.Get))
{
    <p>
        Find by name: @Html.TextBox("searchString", ViewBag.CurrentFilter as String, new { data_autocomplete_source = Url.Action("QuickSearchMembers", "Members")})
        <input type="submit" value="Search" />
    </p>
}


Note the name of the first argument to Url.Action - this needs to match the name of the method in the jQuery function that is also added to the page:

<!-- below is to autocomplete search text box -->
<script type="text/javascript" language="javascript">
       $(document).ready(function () {
            $("#searchString").autocomplete({
                source: '@Url.Action("QuickSearchMembers", "Members")'
            });
        })
</script>


 To change the styling of the suggested values, you can edit the .css file /Content/themes/base/autocomplete.css

.ui-autocomplete {
    position: absolute;
    top: 0;
    left: 0;
    cursor: default;
    background-color: lightgray;
}


For example, I added an attribute to make the background light grey so the suggestions are easier to read

 So, how does this work? When the user changes the content of the search text box the .autocomplete jQuery method is called, which will then call the public ActionResult QuickSearchMembers( string term )  method from the controller returning a list of suggestions that is displayed on the screen. Due to this, this technique can result in a lot of network traffic as each character entered (or removed) from the text box will result in a call to the server and the response of a Json payload - which is why I think it is a good idea to limit the number of items returned in the Json packet.

An alternative method is to make use of the HTML <datalist> tag and a list of data returned from the controller. see http://www.w3schools.com/tags/tag_datalist.asp for some additional details. This method would reduce network requests to once per page request. However, this would only work if the number of possible suggestions is small otherwise page size would become large and page load times would suffer. (edit - see my next post on the implementation of this method)

One drawback to both methods is that additional data is server up from the web server, which mean additional load and for a cloud hosted application additional cost. So for a high traffic site, you would need to ask if the autocomplete feature is really worth the additional expense.




No comments:

Post a Comment