Auto Complete in Razor MVC with JQuery and LINQ to Entities
February 26, 2011 12 Comments
I had to create an auto-complete text box this week in Razor and didn’t see anything specific to doing it in Razor so I thought I’d throw this out there. I would like to say this post for getting it to work in MVC was very helpful and it didn’t take much effort to move from that to Razor and the Entity Framework.
In my example below I’ll be auto populating the product name based on the existing product names in the database using LINQ to Entities.
Step 1 : Download the JQuery Auto-Complete Library from google code
http://code.google.com/p/jquery-autocomplete/downloads/list
Step 2: Extract the following files into your scripts directory
jquery.autocomplete.js
jquery.autocomplete.css
indicator.gif
You can move them around later but for now lets keep them all in one folder and modify the css file to point to our new location for the loading gif.
background : url(‘/scripts/indicator.gif’) right center no-repeat;
Step 3 : Build your controller class
I’m selecting the top 10, distinct where the product name contains the typed in text.
using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.Web.Mvc; using MyNameSpace.Models; namespace MyNameSpace.Controllers { public class AutoCompleteController : Controller { MyNameSpaceEntities mhgDb = new MyNameSpaceEntities(); public ActionResult ProductName(string q) { var productNames = (from p in mhgDb.Products where p.ProductName.Contains(q) select p.ProductName).Distinct().Take(10); string content = string.Join<string>("\n", productNames); return Content(content); } } }
Step 4: Add the JS and CSS files to your view
<script src="@Url.Content("~/Scripts/jquery.autocomplete.js")" type="text/javascript"></script> <link href="@Url.Content("~/Scripts/jquery.autocomplete.css")" rel="stylesheet" type="text/css" />
Step 5 : Bind the Textbox to the Controller
<div class="editor-label"> @Html.LabelFor(model => model.ProductName) </div> <div class="editor-field"> @Html.EditorFor(model => model.ProductName) @Html.ValidationMessageFor(model => model.ProductName) </div> <script type="text/javascript"> $(document).ready(function () { $("#ProductName").autocomplete('@Url.Action("ProductName", "AutoComplete")', { minChars: 3 }); }); </script>
Step 6 : Build and Test
That’s all, there’s some tips around styling and parameters for autocomplete in the link to the other blog post I linked to above. I’d be interested in seeing a better LINQ statement if anyone has one.
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using MyHobbyGear.Models;
namespace MyHobbyGear.Controllers
{
public class AutoCompleteController : Controller
{
MyHobbyGearEntities mhgDb = new MyHobbyGearEntities();
//
// GET: /AutoComplete/
public ActionResult Manufacturer(string q)
{
var manufacturers = (from p in mhgDb.Products where p.ManufacturerName.Contains(q) select p.ManufacturerName).Distinct().Take(10);
string content = string.Join<string>(“\n”,manufacturers);
return Content(content);
}
public ActionResult ProductName(string q)
{
var productNames = (from p in mhgDb.Products where p.ProductName.Contains(q) select p.ProductName).Distinct().Take(10);
string content = string.Join<string>(“\n”, productNames);
return Content(content);
}
}
}
Very cool! If you wanted to do your LINQ query in one expression you could do something like this:
But then there is nothing wrong with your code as is – I think that your existing code might be a bit faster with
string.Join
. Cool article though!Lambda expressions make me feel dumb. 🙂 Not as bad as regex but I’m still wondering why that works.
I got make myself start using them.
How is the q value getting passed to the controller?
the jquery component does that with the binding.
$(document).ready(function () {
$(“#ProductName”).autocomplete(‘@Url.Action(“ProductName”, “AutoComplete”)’, { minChars: 3 });
});
Thanks for the reply, i am not getting it to work, but i think i might be using the incorrect autocomplete jscript 😛
thanks man, your example saved my day, thanks again…..
Hello,
I am trying to implement this in my MVC 3 project. (I am new to MVC).My question is:
In STEP 3 above, Do I have to create that as a New Controller or add it to an existing controller?
My App that I am using to learn already has a Prducts controller that looks like this:
public class ProductController : Controller
{
private SampleStoreContext db = new SampleStoreContext();
//
// GET: /Product/
public ViewResult Index()
{
var products = db.Products.Include(p => p.Category);
return View(products.ToList());
}
//
// GET: /Product/Details/5
public ViewResult Details(int id)
{
Product product = db.Products.Find(id);
return View(product);
}
//
// GET: /Product/Create
public ActionResult Create()
{
ViewBag.CategoryID = new SelectList(db.Categories, “CategoryID”, “Name”);
return View();
}
//
// POST: /Product/Create
[HttpPost]
public ActionResult Create(Product product)
{
if (ModelState.IsValid)
{
db.Products.Add(product);
db.SaveChanges();
return RedirectToAction(“Index”);
}
ViewBag.CategoryID = new SelectList(db.Categories, “CategoryID”, “Name”, product.CategoryID);
return View(product);
}
//
// GET: /Product/Edit/5
public ActionResult Edit(int id)
{
Product product = db.Products.Find(id);
ViewBag.CategoryID = new SelectList(db.Categories, “CategoryID”, “Name”, product.CategoryID);
return View(product);
}
//
// POST: /Product/Edit/5
[HttpPost]
public ActionResult Edit(Product product)
{
if (ModelState.IsValid)
{
db.Entry(product).State = EntityState.Modified;
db.SaveChanges();
return RedirectToAction(“Index”);
}
ViewBag.CategoryID = new SelectList(db.Categories, “CategoryID”, “Name”, product.CategoryID);
return View(product);
}
//
// GET: /Product/Delete/5
public ActionResult Delete(int id)
{
Product product = db.Products.Find(id);
return View(product);
}
//
// POST: /Product/Delete/5
[HttpPost, ActionName(“Delete”)]
public ActionResult DeleteConfirmed(int id)
{
Product product = db.Products.Find(id);
db.Products.Remove(product);
db.SaveChanges();
return RedirectToAction(“Index”);
}
protected override void Dispose(bool disposing)
{
db.Dispose();
base.Dispose(disposing);
}
}
Thanks. I am doing Code First
You can use an existing controller you just need a custom action and you have to specify the controller / action when calling JQuery.
In my instance productname in the controller and autocomplete is the action.
$(document).ready(function () {
12 $(“#ProductName”).autocomplete(‘@Url.Action(“ProductName”, “AutoComplete”)’, { minChars: 3 });
13 });
I spent almost two nights in trying to implement auto complete feature, and your example finally worked. thanks a lot. Now I have two supplementary questions
1. why the break point that i had in the Controller method which is responsible for returning Json does not work, beside the fact that auto complete is working. Following is the code from my Controller method
[HttpPost]
public JsonResult AutocompleteAsync(string term)
{
RateACandidateEntities db = new RateACandidateEntities();
return Json((from cd in db.Candidates where cd.CName.StartsWith(term) select cd.CName).ToArray(), JsonRequestBehavior.AllowGet);
}
2. what is the difference between your implementation and this other way as following
$(function () {
$(“input.FamousPerson-List”).autocomplete({
source: function (request, response) {
$.ajax({
url: “/FamousPeople/FPPAutoComplete”,
type: “POST”,
dataType: “json”,
data: {
searchText: request.term,
maxResults: 12
},
success: function (data) {
response($.map(data, function (item) {
return {
value: item.DisplayName
}
}))
}
});
}
});
});
When I do the call $(“#ProductName”).autocomplete(‘@Url.Action(“ProductName”, “AutoComplete”)’, { minChars: 3 });
The razor does not do what I expected. I get a “/” and the jquery never calls my controller
I face the same problem. try following thing it works for me. 🙂
$(“#ProductName”).autocomplete({source: ‘@Url.Action(“ProductName”, “AutoComplete”)’}, { minChars: 3 });
{soruce: is missing i guess…
Regards,
Hemant Shelar
I’m wondering how would you attach the autocomplete function to a dynamically generated input ? So the name cannot be used in the javascript section like in your example : ” $(“#completeMe”).autocomplete({ . … ” ;
My idea is to call a javascript function when text is changing in the dynamically generated text input :
x.Name,new { onkeypress=”doSomething(this)”}) %>
and in the doSomething() method i will have a reference to the sender input given by “this” .
but further . . i don;t have a clue how to attach the autocomplete function to the sender . .
Do you have any idea ?
Thanks in advance