I implemented an autocomplete search box on my asp.net mvc4 site. I am currently able to have the box return results that update as i type in the search box. I also am dynamically generating "category" buttons based on the result "type IDs" and inserting them in a header that appears when the autocomplete produces results.
I want to introduce functionality that goes like this: when the user clicks the category button, the existing autocomplete results get filtered further so only results of that "type ID" are shown. After that, if the user wants to see all of the results matching the search string again, they can click the "All" button.
To see a working version of this, please check out the search box on Discogs.com. I have also pasted a screenshot of this widget below, for reference.
How can I implement this? I can't find any stackoverflow posts about this because I don't know how to phrase my question.
My code is below. In it, I already have a functioning autocomplete, and I have the portion that dynamically generates the category buttons. Now what I need help with is finding a design pattern to further filter the autocomplete results when I click the category buttons that were dynamically generated.
@model myproject.Models.Search_Term
@Scripts.Render("~/bundles/jquery")
<script type="text/javascript">
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// autopopulate input boxes
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//detect the browser resize and close the Autocomplete box when that event is triggered
$(window).resize(function() {
$("#searchBox").autocomplete("close");
});
//helper method for autopopulate.
//https://stackoverflow.com/questions/2435964/how-can-i-custom-format-the-autocomplete-plug-in-results
//this helps in creating a autocomplete menu with custom HTML formatting
function monkeyPatchAutocomplete() {
$.ui.autocomplete.prototype._renderItem = function( ul, item) {
var inner_html = '<img src="' + item.imgPathSmall + '">';
return $("<li>")
.data("ui-autocomplete-item", item)
.append(inner_html)
.appendTo(ul);
};
}
// look up search term
$(document).ready(function () {
//call this to enable the autocomplete menu with custom HTML formatting
monkeyPatchAutocomplete();
//trigger autocomplete
$("#searchBox").autocomplete({
source: function (request, response) {
$.ajax({
url: "/Explore/SearchAutocomplete",
type: "POST",
dataType: "json",
data: { search: request.term },
success: function (data) {
response($.map(data, function (item) {
return {
objectName: item.ObjectName,
detail1: item.Detail1,
detail2: item.Detail2,
detail3: item.Detail3,
imgPathSmall: item.Image_Data_SmallPad_string,
objectType: item.ObjectType,
objectID: item.ObjectID,
image_Data_SmallPad: item.Image_Data_SmallPad,
image_MimeType_SmallPad: item.Image_MimeType_SmallPad
};
}))
}
})
},
select: function (event, ui) {
event.preventDefault();
//redirect to result page
var url;
switch (ui.item.objectType) {
case 1:
url = '@Url.Action("Category1", "Explore")?i=' + ui.item.objectID;
break;
case 2:
url = '@Url.Action("Category2", "Explore")?i=' + ui.item.objectID;
break;
case 3:
url = '@Url.Action("Category3", "Explore")?i=' + ui.item.objectID;
break;
case 4:
url = '@Url.Action("Category4", "Explore")?i=' + ui.item.objectID;
break;
case 5:
url = '@Url.Action("Category5", "Explore")?i=' + ui.item.objectID;
break;
case 6:
url = '@Url.Action("Category6", "Explore")?i=' + ui.item.objectID;
break;
case 7:
url = '@Url.Action("Category7", "Explore")?i=' + ui.item.objectID;
}
window.location.href = url;
}
}).data("ui-autocomplete")._renderMenu = function (ul, items) {
//------------------------------------------------------------------------------------
//Append the header
//------------------------------------------------------------------------------------
var header = `
<li>
<div class='acmenu_header'>
<div class="btn-group special" role="group" aria-label="...">
<button type="button" class="btn btn-default btn-xs">All</button>
`;
//helps determine the category buttons to generate
var categories = [];
$.each(items, function (index, item) {
if (item.objectType) {
switch (item.objectType) {
case 1:
categories.push(1);
break;
case 2:
categories.push(2);
break;
case 3:
categories.push(3);
break;
case 4:
categories.push(4);
break;
case 5:
categories.push(5);
break;
case 6:
categories.push(6);
break;
case 7:
categories.push(7);
}
}
});
//helps determine the category buttons to generate
var uniqueCategories = [...new Set(categories)];
var arrayLength = uniqueCategories.length;
//generate the category buttons within the header
for (var i = 0; i < arrayLength; i++) {
switch (uniqueCategories[i]) {
case 1:
header = header + '<button type="button" class="btn btn-default btn-xs">Category1</button>'
break;
case 2:
header = header + '<button type="button" class="btn btn-default btn-xs">Category2</button>'
break;
case 3:
header = header + '<button type="button" class="btn btn-default btn-xs">Category3</button>'
break;
case 4:
header = header + '<button type="button" class="btn btn-default btn-xs">Category4</button>'
break;
case 5:
header = header + '<button type="button" class="btn btn-default btn-xs">Category5</button>'
break;
case 6:
header = header + '<button type="button" class="btn btn-default btn-xs">Category6</button>'
break;
case 7:
header = header + '<button type="button" class="btn btn-default btn-xs">Category7</button>'
}
}
header = header + `
</div>
</div>
</li>
`;
$(ul).append(header);
//------------------------------------------------------------------------------------
//append the autocomplete results
var that = this;
var currentCategory = "";
var currentCategoryLabel = "";
$.each(items, function (index, item) {
if (item.objectType != currentCategory) {
if (item.objectType) {
switch (item.objectType) {
case 1:
currentCategoryLabel = "Category1";
break;
case 2:
currentCategoryLabel = "Category2";
break;
case 3:
currentCategoryLabel = "Category3";
break;
case 4:
currentCategoryLabel = "Category4";
break;
case 5:
currentCategoryLabel = "Category5";
break;
case 6:
currentCategoryLabel = "Category6";
break;
case 7:
currentCategoryLabel = "Category7";
}
ul.append("<li class='ui-autocomplete-category'>" + currentCategoryLabel + "</li>");
}
currentCategory = item.objectType;
}
that._renderItem(ul, item);
});
//append the footer
var footer = `
<li>
<mark><span class="glyphicon glyphicon-cog" aria-hidden="true"></span> Advanced search</mark>
</li>
`;
$(ul).append(footer);
};
})
</script>
@using (Html.BeginForm("Search", "Explore", FormMethod.Post, new { id = "searchFormNavbar", @class = "nav navbar-form navbar-left", enctype = "multipart/form-data" }))
{
@Html.AntiForgeryToken()
<div class="input-group" id="searchDiv">
@Html.EditorFor(m => Model.SearchTerm, new { htmlAttributes = new { @class = "form-control", @id = "searchBox", placeholder = "Search x, y, z, and more...", style = "width:100%; min-width: 380px;" } })
<div class="input-group-btn">
<button id="searchBtn" class="btn btn-default" type="submit" style="color:steelblue">
<span class="glyphicon glyphicon-search" aria-hidden="true"></span>
</button>
</div>
</div>
}