Introduction
SearchBoxAutoComplete
is auto-completion jQuery widget that enables you to search the categorized data through the hierarchies, i.e., Brand/Categories/Products or Category/Brands/Products.
Background
Many Virtual Stores are offering Products classified by Categories and Brands. Usually there are a lot of Categories, Sub-Categories and Brands, and a user has difficulties to find the appropriate Category, Brand or Product.
SearchBoxAutoComplete
widget enables a user to quickly find and select the wanted Category or Brand. By entering 2 or more characters, the user can filter down the list to better matches.
After user selects Category, the widget offers a drop-down list with all the Brands that products of the selected Category belong.
If user selected the Brand, widget would offer drop-down list with all the Categories that Brand’s Products belong to.
User can select multiple items from the offered list, or enter new characters to start a new hierarchy. Results from multiple hierarchies are aggregated at the server.
Here is the sample of 2 hierarchies: categ1: brand1: brand2; categ2: brand3:
SearchBoxAutoComplete jQuery Widget
jQuery Widgets resolve the problem that jQuery Plugins have with associating an JavaScript Object with a DOM element, avoiding circular references and memory leaks.
SearchBoxAutoComplete
“jquery.ui.searchboxautocomplete.js” widget uses standard “jquery.ui.autocomplete.js” widget.
Instead of hacking the existing widget, SearchBoxAutoCompelete
modifies the behavior of the existing widget by extending or overriding/sub-classing the widget properties, methods and events.
$.widget("ui.searchboxautocomplete", $.ui.autocomplete, {
_create: function () {
...
},
_init: function () {
$.ui.autocomplete.prototype._init.apply(this, arguments);
this.element.bind("searchboxautocompleteclose", function () {
self.options.offeringSecondLevel = false;
});
…
},
options: {
select: function (event, ui) { … },
cache: {},
autocompleteRows: [],
separator: ": ",
…
}
}
SearchBoxAutoCompleteRows widget uses Ajax JSON call to get the data from the server.
List of: AutocompleteRow { category: 'Brands', label:'Bikes', value:11 }
is serialized at the client by:
JSON.stringify(this.options.autocompleteRows).
At the server, it is deserialized with:
var selectedRows = new JavaScriptSerializer().Deserialize<List<AutocompleteRow>>(acRows);
Form
<form id="searchbox" action="/search/productlist" method="post">
<input type="text" id="search" value=""/>
<input id="searchbutton" type="submit" value="Search"/>
<input type="hidden" id="selectedAutocompleteRows" />
</form>
$("#search")
.setSearchText(autocompleteRows)
.searchboxautocomplete({
autocompleteRows: autocompleteRows,
source: function (request, response) {
var term = $.ui.searchboxautocomplete.extractLast(request.term);
var data = this.options.getCachedData(term);
if (data != null) {
response(data);
return true;
}
var that = this;
this.options.lastXhr =
$.getJSON("/Search/GetBrandsAndCategories",
{term:term, acRows:JSON.stringify(this.options.autocompleteRows)},
function (data, status, xhr) {
that.options.setCachedData(term, data);
if (xhr === that.options.lastXhr) {
response(data);
}
})
},
search: function () {
if (term.length < 2) {
return false;
}
}
})
Data are received from server by Ajax $.getJSON()
call. Arguments sent to the server are:
- Last search term
- Array of already selected rows
This is done by:
{ term: term, acRows: JSON.stringify(this.options.autocompleteRows) }
Data returned from the server are rendered by function response(data)
called upon successful Ajax call.
Default fields are: category, label, value. You can override _renderItem
function and display your fields.
Data returned from the server are in JSON format, something like this:
var data = [
{category:"Categories", label:"Mountain Bikes", value:"1"},
{category:"Categories", label:"Road Bikes", value:"2"},
{category:"Categories", label:"Touring Bikes", value:"3"},
{category:"Brands", label:"Mountain Bikes", value:"12"}
]
Here is the MVC 2.0 server method that returns the data in JSON format:
public JsonResult GetBrandsAndCategories(string acRows, string term)
{
var selectedRows = new JavaScriptSerializer()
.Deserialize<List<AutocompleteRow>>(acRows);
var firstRow = selectedRows[0];
term = term.Trim().ToLower();
if (term == "secondlevel")
{
var list = new List<AutocompleteRow>();
if (firstRow.category == "Brands")
{
var brand = Brand.GetById(int.Parse(firstRow.value));
list = brand.Categories
.Select(c => new AutocompleteRow {
category = "Categories", label = c.Name, value = c.Id.ToString()
})
.ToList<AutocompleteRow>();
}
else
{
}
return Json(list, JsonRequestBehavior.AllowGet);
}
}
Demo Data Repository
The structure of data is usually like this:
<categories>
<category id="3" name="Clothing">
<sub-categories>
<sub-category id="22" name="Shorts">
<products>
<product id="841" name = "Men's Sports Shorts, S">
<brands>
<brand id="29" name="Integrated Sport" price="20.00">
<brand id="102" name="Fitness Association" price="19.80">
</brands>
</product>
</products>
</sub-category>
</sub-categories>
</category>
</categories>
History
- 28th June, 2011: Initial post