So I want to create a popup search window using ASP.Net MVC 5 and Telerik’s Kendo UI. I am using the @HtmlHelper technique in my code (less JavaScript for me to write). I want to be able to select a search term in an AutoComplete box and then find all the records that match the term and display them in a Grid in the Window.
This post has a lot of code in it because there are a lot of moving parts.
Here’s what I want the finished popup search window to look like:
1. Create a Kendo UI Enabled ASP.Net MVC 5 Project
Go ahead and create a new ASP.Net MVC 5 project, then add the Telerik artifacts as described here.
Note: The instructions say to put the @Scripts.Render(“~/bundles/kendo”) after the @Scripts.Render(“~/bundles/jquery”), which is true. What it doesn’t tell you is that BOTH lines need to move to the top of the page, in the <head> section or the Kendo UI components won’t render.
Before we begin creating the UI we need something to search in. I am using a simple number generator to create 10,000 items to search through. Each SearchResult will have an Id (which is a number from 1 to 10,000), and a Description (which is the same number, prefixed with the letter ‘A’).
The SearchResult class looks like this:
namespace Experiment.Web.Models.Search { public class SearchResult { public int Id { get; set; } public string Description { get; set; } } }
And the Searcher itself:
namespace Experiment.Web.Models.Search { public class Searcher { private readonly List<SearchResult> list = new List<SearchResult>(); private IQueryable<SearchResult> cachedList; public Searcher() { FillList(); } public string SearchTerm { get; set; } public IQueryable<SearchResult> GetSearchResults(string searchTerm) { if (String.IsNullOrEmpty(searchTerm)) { //Return empty list. Option: Or return everything. return new List<SearchResult>().AsQueryable<SearchResult>(); } if (searchTerm != this.SearchTerm) { this.SearchTerm = searchTerm; cachedList = list.Where(r => r.Description.ToString().Contains(searchTerm)).AsQueryable<SearchResult>(); } return cachedList; } private void FillList() { //TODO: Fill your List properly here for (int i = 0; i < 10000; i++) { list.Add(new SearchResult { Id = i , Description = "A" + i.ToString()}); } } } }
Now we have something to search, we’ll need to setup the page that will host the Window.
2. Set up the Host Page for the Search Window
I will use the _Layout page for this. We could use a simple button for this, but that’s not very “real-world” so instead, here is the top navigation bar as it currently renders in my app:
And here’s the code that makes that work (Note: This is not using a Kendo UI control yet, but this works.):
<div class="navbar navbar-inverse navbar-fixed-top"> <div class="container-fluid"> <!-- Brand and toggle get grouped for better mobile display --> <div class="navbar-header"> <button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#mainNavbar"> <span class="sr-only">Toggle navigation</span> <span class="icon-bar"></span> <span class="icon-bar"></span> <span class="icon-bar"></span> </button> @Html.ActionLink("Experiment", "Index", "Home", new { area = "" }, new { @class = "navbar-brand" }) </div> <div class="navbar-collapse collapse" id="mainNavbar"> <ul id="mainMenu" class="nav navbar-nav"> <li>@Html.ActionLink("Home", "Index", "Home")</li> </ul> <ul id="searchButtonArea" class="nav navbar-nav navbar-right"> <li><a href="#" class="dropdown-toggle" role="button"><span class="glyphicon-search glyphicon" id="showSearchWindow"></span></a></li> </ul> <p class="nav navbar-text navbar-right">Hello, @User.Identity.Name!</p> </div> </div> </div> Notice the use of the span and the glyphicon-search to put the magnifying glass in the top nav bar. This was hard to figure out, but if you take the sample code you can put several buttons off to the right. Now to wire up the search icon:
<script> $(document).ready(function () {$("#showSearchWindow").bind("click", function () { $("#searchWindow").data("kendoWindow").open();});}); </script>
Finally, we write the code to render the Window:
@(Html.Kendo().Window() .Name("searchWindow") .Title("Search") .Visible(false) .Actions(x => x.Close()) .LoadContentFrom("SearchWindow", "SearchWindow") .Iframe(true) .Draggable() .Resizable() .Position(x => { x.Left(10); x.Top(20); }) .Width(500) .Height(567) )
So far, you should have a Home page with a Nav bar where the Search icon pops up a Window. (Note: You will get an error at this stage because you don’t have the Action defined for the LoadContentFrom property.)
3. Create the Search Window Controller and View
Right click in the Controllers folder and add a new empty MVC 5 Controller named SearchWindowController.
using Experiment.Web.Models.Search; using System; using System.Linq; using System.Web.Mvc; using Kendo.Mvc.UI; using Kendo.Mvc.Extensions; namespace Experiment.Web.Controllers { public partial class SearchWindowController : Controller { public ActionResult SearchWindow() { return PartialView(); } } }
You’ll also need to create a simple search window view. Right click in the controller’s code and select Add View. Choose MVC 5 view and name it SearchWindow. Be sure to select ‘Create as a partial view’. Leave the view empty for now.
You should be able to test it now without any errors. Run the app and click the search icon, the empty Window should appear.
4. Adding the Kendo AutoComplete Box
The next step is to add the Kendo AutoComplete box to our Kendo Window.
First, we’ll bind the SearchWindow.cshtml page to our Search model by adding this declaration to the top of the file:
@using SampleSearchWindow.Models.Search;
Then, we’ll add a content Div to the SearchWindow.cshtml page:
<body style="padding: 10px;"> <div class="panel panel-body" style="padding: 0; margin-bottom: 0"> </div> </body>
Then we can add the code for the AutoComplete box:
@(Html.Kendo().AutoComplete() .Name("mySearchBoxInWindow") .MinLength(3) .DataTextField("Description") .DataSource(source => { source.Read(read => { read.Action("AutoComplete", "SearchWindow") .Data("getAutoCompleteValueInWindow"); }).ServerFiltering(true); }) .Events(e => e.Select("mySearchBoxInWindow_Select").Change("mySearchBoxInWindow_GetValueAndRefresh")) )
Note the places where we link to the AutoComplete action itself, bind to the DataSource and to the events for Select and Change.
In order to get the box to work properly when the user types a value or selects a value from the dropdown that appears after they type the first few characters, we need to keep a Hidden value where we record the search term, and then use JavaScript to keep that value in sync with what is selected in the AutoComplete box.
Add the Hidden search term above the AutoComplete box:
@Html.Hidden("hiddenSearchTerm")
Then add the following JavaScript above the Hidden search term:
<script> function mySearchBoxInWindow_Select(e) { var dataItem = this.dataItem(e.item.index()); $("#hiddenSearchTerm").val(dataItem.Description); mySearchBoxInWindow_Refresh(); } function mySearchBoxInWindow_GetValue() { $("#hiddenSearchTerm").val($("#mySearchBoxInWindow").val()); } function mySearchBoxInWindow_GetValueAndRefresh() { mySearchBoxInWindow_GetValue(); mySearchBoxInWindow_Refresh(); } function mySearchBoxInWindow_Refresh() { $("#searchResultsGrid").data('kendoGrid').dataSource.read(); $("#searchResultsGrid").data('kendoGrid').refresh(); } </script>
Finally, after the AutoComplete box, add the JavaScript that pulls the value from the Hidden value and the one that puts it into the Data element of the AutoComplete’s DataSource:
<script type="text/javascript"> function getAutoCompleteValueInWindow() { return { term: $("#mySearchBoxInWindow").val() }; } function getHiddenSearchTermValueInWindow() { return { term: $("#hiddenSearchTerm").val() }; } </script>
With all this code in place, we need to add the AutoComplete method to the SearchWindowController.cs so that we can populate the AutoComplete box’s dropdown list:
public ActionResult Autocomplete(string term) { Searcher searcher = new Searcher(); var searchResults = searcher.GetSearchResults(term); return Json(searchResults, JsonRequestBehavior.AllowGet); }
Now, we have the Action defined, we have the DataSource defined, we have some JavaScript to keep everything in sync. You would think we could test it now. But if you try, you will see the AutoComplete box doesn’t even render properly.
GOTCHA. The Kendo Window control does NOT inherit the includes from the shared/_layout.cshtml, nor from the host page. Instead, you must declare all your includes in the SearchWindow.cshtml page itself. So, at the top, below the @using and above the <body> tag, add these two lines to include all the styles and scripts that make the Kendo magic happen:
@Styles.Render("~/Content/css", "~/Content/kendo/css") @Scripts.Render("~/bundles/modernizr", "~/bundles/jquery", "~/bundles/kendo")
Now, finally, we should have some run-able, testable code.
Give it a whirl. You should be able to run the project, view the Search Window, see the AutoComplete box, and type a sample search string that displays results in the drop down (remember our sample searchable data is A1 to A10000, so a value of A22 should return results in the drop box).
5. Add the Results Panel to the Search Window
This is the last step. We want to show the items that match the search term in a list panel (Kendo Grid).
So first, we add the Grid to the SearchWindow.cshtml page, after the last script, right before the last </div> tag:
<div id="searchResultsList" role="grid"> <div><h3>Search Results</h3></div> @(Html.Kendo() .Grid<SearchResult>() .Name("searchResultsGrid") .DataSource(datasource => datasource.Ajax().Read(read => read.Action("GetSearchResults", "SearchWindow").Data("getHiddenSearchTermValueInWindow"))) .Pageable(p => p.ButtonCount(5)) .Sortable() ) </div>
Now, we add the DataSource to the SearchWindowController.cs, like so:
public ActionResult GetSearchResults([DataSourceRequest]DataSourceRequest request, string term) { Searcher searcher = new Searcher(); IQueryable<SearchResult> searchResults = searcher.GetSearchResults(term); DataSourceResult result = searchResults.ToDataSourceResult(request); return Json(result); }
Note: If anyone knows how to cache the Searcher class so that we don’t have to instantiate it in both the Autocomplete method and the GetSearchResults method, please let me know in the comments.
And there you have it. The completed project with a Kendo AutoComplete box populating a Kendo Grid, with both in a Kendo Window. Woohoo.
Here is the project source code so you can load it and play with it yourself.
Have fun coding!
-Simon
Simon
Can’t find the code, please send it to me
thanks
Which code can’t you find? I think the only copy of the code is in the article.
Oh. That code. I see now. The dropbox link is broken.
I updated the link. Thanks!
Thank you very much very helpful much appreciate