Auto Complete in Razor MVC with JQuery and LINQ to Entities


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;
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);
}

}
}

About Kevin Buckley
.Net web developer with a lot of experience in CMS. Currently working at Sitecore as Solutions Engineer.

12 Responses to Auto Complete in Razor MVC with JQuery and LINQ to Entities

  1. Andrew Hare says:

    Very cool! If you wanted to do your LINQ query in one expression you could do something like this:

    var content = 
        mhgDb.Products
            .Where(p => p.ProductName.Contains(q))
            .Select(p => p.ProductName)
            .Aggregate("", (allNames, nextName) => allNames + "\n" + nextName);

    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!

  2. 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.

  3. Robin Curtis says:

    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 });
      });

      • Robin Curtis says:

        Thanks for the reply, i am not getting it to work, but i think i might be using the incorrect autocomplete jscript 😛

  4. manish says:

    thanks man, your example saved my day, thanks again…..

  5. yousaid B. says:

    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

  6. 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 });

  7. Ali says:

    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
    }
    }))
    }
    });
    }
    });

    });

  8. Robbye says:

    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

    • Hemant Shelar says:

      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

  9. Razvan says:

    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

Leave a reply to Kevin Buckley Cancel reply