Tuesday, February 9, 2010

ASP.NET MVC: Create Facebook-like search box

By Hristo Yankov
In today's article I will show you how to create your own Facebook-like (or LinkedIn-like) search box. If you haven't seen what it looks like, here is an example:

You will notice that it features: autocomplete, icon, matches what you type and when you click a result item, it takes you to the appropriate page. That's what our control in MVC will feature too. We will implement this in ASP.NET MVC2 and jQuery.

The Plan:
We will create an attribute and use it to decorate the action methods of our controllers. When we decorate an action, we will be adding metadata, such as the display name it should appear with in the search results and an optional icon. When the user types something in the search control, we will use jQuery to POST to a special controller, which will be enumerating all action methods in all controllers. While enumerating, it will be looking for a match between the search query and the metadata of each 'indexed' action method.


Note: You can download the working demo project from here

Visit the project page here: http://yankov.us/#Projects

The Attribute
Ok, let's start with the attribute. In your Code folder (if you don't have one - go ahead and create it) add a new class and name it 'ActivitySearchAttribute'. It will have two string properties - Text and ImageUrl. Implement it like that:

This is the attribute we will be using to decorate our action methods in the controllers. That way we will mark them as 'searchable'.

Action-Method Decorations
Let's create a couple of dummy controllers, with a couple of actions in each of them and decorate them with the newly created attribute. Like this:


As you can see, the icon property ("group.png" in the example) in the metadata is optional. Non-decorated action methods will not be returned as search results.

The Search Controller
Now let's implement the search controller. Basically, what it will be doing is: receive a query string (e.g. "the", as in the first image of this post), get all controllers, enumerate the action method of each controller, match the 'Text' property of the 'ActivitySearch' attribute (which we use to decorate the action) and then return an array of the matches with the appropriate URL to each result. Just like Facebook. I won't paste the code here, as it is big, but you will find it in the attached sample project. The file is in Controllers and is named "SearchController.cs".

Json Response
The search will be a dynamic thing. While the user types, we will be sending a request to a controller and we will be receiving a response. We need to create a class which we will serialize to JSON and use it as a response. In your Code folder, create another class and name it 'ActivitySearchResultItem'. It will have three properties: Text, Url and Image. Implement it like that:

The Search controller will be returning an array of those, to the jQuery UI, as a JSON response.

The JavaScript / jQuery
Now, when we have the back-end, we just need to implement the front-end in jQuery and html. Note that our solution will be using a 3rd party library - jquery.autocomplete.pack.js
Additionally, we will put our javascript, in a file named "activitySearch.js".

The auto-complete jQuery plugin will take care of attaching to the text-box and submitting/receiving data to/from the back-end controller. We just need to process and format the results. When the user clicks a result, he will be redirected to the URL property associated with the result item (see 'Json Response' above). Like that:

As you can see, the jQuery code expects that if there is an Image associated with the search result it should be in the /Content/images folder.

Here is how to setup your page which will contain the search-box control.

Now, when you have everything in place, start the project and just type in the search box some phrase which you know will hit a result. You should see something like:

The search results are clickable and when clicked, it will take you to the appropriate action. You can optimize the whole solution with various caching settings and techniques.

You can download the whole demo project here.
Read more on this article...
Bookmark and Share

Friday, February 5, 2010

ASP.NET MVC2 - Creating a Display Template

By Hristo Yankov
ASP.NET MVC philosophy is "Skinny Controller, Fat Model, Stupid View". The focus of today's post is "stupid view". What does this mean? Well, it means that you should avoid writing any logic in your views. A view should be as simple as wrapping the model's properties in html tags.

If you are cornered, and must have some kind of a login in your view, it would be nice if it is neatly wrapped in a DisplayTemplate, so your code remains reusable, easy to read and maintain. What follows now is a tutorial on how to create one.

Problem: We have a Model which has a property "RemainingTime", which is an integer and represents the estimated remaining seconds of some operation. We want to neatly format this time in the view, so that if it has a value of, let's say, "5", to be displayed as "5 seconds". If it has value of "70", to be displayed as "1 min, 10 seconds", etc...

Solution: Create a reusable DisplayTemplate and use it to visualize the property.

We start by creating a new ASP.NET MVC2 "empty" project template. We don't need the default authentication and home controllers, so that would do just fine.

As a first step, let's create our Model. We'll call it "RemainingTimeModel" and will have the integer property "EstimatedSeconds". We will name the DisplayTemplate "SecondsFormatting". So we go ahead and decorate the "EstimatedSeconds" property with the UIHint attribute, passing "SecondsFormatting" (which is the name of the DisplayTemplate we want to use) in its constructor. This attribute tells ASP.NET MVC which DisplayTemplate to use by default, for this property. Your code should look like this:

Second step is to create the actual DisplayTemplate. It needs to reside in the "Views/Shared/DisplayTemplates" folder, and be named "SecondsFormatting.ascx". So, create a "DisplayTemplates" folder in your Shared views, then right click it and Add -> View. Check the 'create partial view' checkbox and give it the name we mentioned above. By default, this will create non-strongly typed ViewUserControl. We will extend this, to an integer. Open the "SecondsFormatting.ascx" file and change the Inherits="System.Web.Mvc.ViewUserControl" to Inherits="System.Web.Mvc.ViewUserControl". Now, when you refer to the Model object in this control, you are working with an integer. The Model that will be passed to this DisplayTemplate is the "EstimatedSeconds" property of the "RemainingTimeModel" model.

Now we just need to implement some logic in the control. Let's do this:

Ok, we have the model, which has the seconds property, and we have a DisplayTemplate for it. Now we jsut need to use it. For this purpose, we will create one simple controller, which creates a new instance of the model and initializes it, then passes the model to a view. In the view, we will have: Html.DisplayFor(model => model.EstimatedSeconds). Like that:



See how clean your View remains? No logic in there, just a DisplayFor html helper call.

We can now go ahead and try it out, with a couple of different values. It formats it accordingly. Please note that there are much easier ways to format the time and it was used here just as an example of what we should do if our view requires to have a logic. If we try with 3732 seconds, it should display "1 hours, 2 minutes, 12 seconds".

Now you know how to encapsulate your logic in DisplayTemplates and keep your main Views clean and neat.

You can download the demo project from here.
Read more on this article...
Bookmark and Share