At the time of this post, I am using Visual Studio 2010 Ultimate RC1.
I recently posted an entry on using a common jQuery plugin that added auto-complete functionality to a textbox. The jQuery UI team has just added a component to the library that provided a more streamlined implementation, closer to the feel of other components and with similar syntax for initiation, events and methods.
The widget actually appears to be authored by the same programmer, only with a much stricter focus on adhering to the jQuery style.
This is a great move and the component has really upped the ante for this release as it’s moved to the core jQuery UI library.
Scroll to near the end to get a point-form walk through. The very end of my post includes a project download.
Revamp or Rewrite?
There will be a few people who are locked into a particular build of a component, who inherit some code or are otherwise just interested in an older version for whatever reason. There are also some important changes to get the new autocomplete to work, as well as some things we no longer need to do. Some of those tricks may be relevant to other plugins, so I’m leaving my original post intact.
My last post was also just a list of things you’d need to do differently from a separate walk-through, so I’m taking this opportunity to write a complete run.
Prerequisites
If you want to follow along with this post you’ll need the following goodies:
- Visual Studio 2010
- The jQuery UI library downloaded with the jQuery library (and any theme you like) rolled in
- I usually also grab the latest VSDOC file so that I get intellisense in the IDE.
Basic Setup Tasks
Finally. Some meat! We’re going to do the following:
- Create an ASP.NET MVC project
- Configure the project (overcome a minor template bug)
- Add the JS and theme files to the project
- Create a master page and setup the includes needed
Okay, let’s go. Crack open Visual Studio and navigate to the new project dialog. To filter the list of project types down, select ‘Web’ and then click on ASP.NET MVC 2 Empty Web Application.
Because of an omission in the template for this project type you’ll need to add two lines in the web.config yourself. This is the web.config located in the root of the project. Make sure your assemblies section contains the lines located here.
Next, we need to get jQuery up to speed (the autocomplete widget requires a newer library), and make our site aware of it. In the Solution Explorer, open up the scripts folder and delete the three 1.3.2 files for jQuery.
Drill into your jQuery UI theme download and get the files from the JS directory. Add those to the script folder in your project. Add the VSDOC file as well, if you’re using it.
We also have to get the theme implemented (if you are so inclinded). Right-click on your project and add a new folder called Content. Copy the themes directory from your jQuery download and paste it in the Content folder your just created.
Now we’ll get our project started by adding a master page to the mix. On your Views –> Shared project folder, right-click and add a new item. Select MVC 2 View Master Page from the list and call it Site.Master because all the cool cats do. In the head tag of the page add the scripts we need to light up the plugin.
If you’re using a theme, you’ll also want to include the required style sheets to make everything appear correctly as I have above. You can easily add the above references by dragging them from Solution Explorer to your head section.
A Simple Start
Let’s test at this point to make sure we’re setup correctly. In this section we’re going to do the following:
- Add a Home controller
- Add a default view (Index) to the project
- Create a div that will be styled to reflect a properly configured style sheet
- Add a small block of code to ensure jQuery is rigged up correctly
Right-click on the Controllers project folder and select Add –> Controller. I’ve used HomeController as the name as it’s the convention used by most.
A very simple class is created for you that represents the first controller. At this point, right-click on the method name “Index” and select “Add View…” from the context menu. The default options work for me (Index as the name, not a partial view, not strongly typed, use the master page I just created).
Side note: because the default route is set up for us in Global.asax, our Home controller with the Index action method will be used for requests to the default document on our site.
To make sure our site is rigged with jQuery and the proper styling, add the following markup to the page:
<div id="test-panel" class="ui-state-default">
This panel will disappear on command.</div><script type="text/javascript" language="javascript">
$(function () {
$('#test-panel').click(function(){
$(this).slideUp();
});
});
</script>
We should be good to go for our test! Run the app (F5). You may be prompted to modify the web config…do that and watch the page come up. Clicking on the div shows that jQuery is lit up and we’re good to go. Your page should look similar to this:
If clicking the panel doesn’t make it slide up or you do not have the shading on the div you should go back and make sure you’ve not missed any steps.
Lighting up Autocomplete
I want to demonstrate the basic functionality of the widget, but I also want to ensure that we’re following some of the practices commonly used in MVC projects. Here’s where we’re going to start:
- Clean up our index view
- Add a textbox to the view
- Add a model class and a corresponding repository that returns a fake set of data
- Create a PartialViewResult method that can be used to retrieve the data
- Add the script needed to call our controller’s action and populate the autocomplete box
Start by quickly removing any of the test code we created, but leave the script block in place as we can reuse that. All that should remain inside your content placeholder is the page title and said block. Add an input with an ID of “name-list” to the page and you should be setup like so:
In your Solution Explorer, right-click on Models and add an new class called Person. Add two properties, an int for the PersonId and a string for FullName. This class took about 4 seconds to write as I used the prop code snippet (type “prop” then tab-tab). It’s not a terribly complicated class to begin with, but it’s a great shortcut!
Add another class to the Model folder called PersonRepository. Create an internal method in it of type List<Person> called FindPeople. The method should accept a string for the search text and an int for the max number of rows to return.
This is just a simple method with a LINQ query to filter the list. We also cap the number of results with the Take() method.
I have omitted some code above; what I have there is a whole bunch of random names added to the names list. The code is in the project download and I got the random names from this site.
With our model and repository in place, we’ll next create the method to be called by the autocomplete plugin to get our list of results.
Side note: We have to be aware that binding will occur to the parameters of the post automatically for us and that reflection is used to map the variables. This is case sensitive and, at the current time, there is no tooling support to make sure we get this right. Your request will just fail in transit and you won’t see anything in the browser. If you’re experiencing this, I highly recommend downloaded FireFox and FireBug to capture and view the requests/responses at play.
I find the following interesting points to mention of the following code:
- We are using an HttpPost to help prevent scripting attacks or data exposure from direct JSON queries
- We use a JsonResult as the return type on the method
- Json() is used to convert our List<Person> – the response type from the repository’s FindPeople method – to the proper JSON notation
- The default behaviour of Json() does not allow GET (but you can override this if required)
And finally, we’re going to write a short burst of JavaScript to complete the mix. At this point we’re only specifying the source but here are some observations:
- We use a function as the ‘source’ property to post data to our controller action
- The function uses an ajax call with the path to the action
- We specify that we’re using POST and that the data is formatted as JSON data
- We define a success method to map the data of the response back to an item array of our format
- label and value properties are used by plugin
- we can store additional data in the result (for example, the id) for later use
The contents of the script block should be as follows:
That’s it?
Yeup. That’s it. Press F5 to run the application and you’ll be able to start auto-completing some things that you type.
Here’s an overview of the steps required, without the bits for the setup testing:
- Create the VS2010 ASP.NET MVC 2 empty project
- Correct web.config’s assembly references
- Add the appropriate jQuery and jQuery UI libraries to the project
- Create a master page and add the script and CSS references
- Create a Home controller with an Index action
- Implement a repository that accepts search criteria and returns model data
- Write a JsonResult method with an HttpPost attribute which accepts search parameters and calls the repository
- Use jQuery to rig-up a text input with the call to the controller action
But what if you want to do something interesting with the result of the user’s selection?
Good question, and it’s what got me started on this whole thing.
The Fun Bits
If you are working with a data-driven site (and likely you are, if you’re in any modern web programming environment) you know that IDs are king.
Luckily we are able to store as complex an object as required in the JSON result. The PersonId from my Person model was stored in the UI element, just as was the display name.
Here is the success function extracted to show the ID getting stored:
So, how do we get it out? Autocomplete actually keeps track of the array for us and allows us to access the selected item when the select event fires. We define a function for this event and pass it in as one of the options for Autocomplete with a couple of parameters. We can then access any properties we attached to the array when we parsed it above:
Now, simply access the ui.item object and any of the properties you setup in the success parser will be available. You can do much more interesting things than just alert(), obviously, but this demonstrates how easy it is to use the plugin with dynamic data where IDs are required.
Conclusion
The addition of Autocomplete as a widget to jQuery UI is a welcome and surprisingly mature component. You will find that with a few setup points and relatively painless coding you will be able to add AJAX-enabled autocomplete functionality to a textbox. It’s also clear that ASP.NET MVC is well suited to integrate with this type of behaviour.