MVC 4.0: Ajax Search with JSON and Client-Side Templates

I talked before about Ajax search but it was using Helper Methods, here we will see how to implement Ajax Search using JSON and Client-Side Templates.

Mustache Client-Side Template

The following code is an example using Mustache, a template library we will use in this post:

<span class="detail">
     Rating: {{AverageReview}}
     Total Reviews: {{TotalReviews}}
</span>

This template would work against an object with AverageReview and TotalReviews properties.
When rendering templates with Mustache, the templates place the values for those properties in their proper location. You can also render templates against an array of data. More documentation for Mustache is available at https://github.com/janl/mustache.js.

To begin writing templates, you can include a script reference to Mustache in the layout view:


<script type="text/javascript" src="~/Scripts/mustache.js"></script>

Now this is where templates come into play. A template is markup embedded inside a script tag. The following code shows a template, as well as the search result markup where the results should display:

<script id="artistTemplate" type="text/html">
<ul>
        {{#artists}}
	<li>{{Name}}</li>

        {{/artists}}</ul>

</script>
<div id="searchresults"></div>

Notice that the script tag is of type text/html.

Modifying the Search Form

We will not use Ajax.BeginForm helper method instead we will write it from scratch.

<form id="artistSearch" method="get" action="@Url.Action("ArtistSearch", "Home")">
    <input type="text" name="q" data-autocomplete-source="@Url.Action("QuickSearch", "Home")" />
    <input type="submit" value="search" />
    <img id="ajax-loader" src="~/Content/Images/ajax-loader.gif" style="display:none"/>
</form>

Without the helper you’ll also need to write your own JavaScript code to request the search result from the server. You’ll place the following code inside your view or place it in outside script file:

$("#artistSearch").submit(function (event) {
    event.preventDefault();
    var form = $(this);
    $.ajax({
        url: form.attr("action"),
        data: form.serialize(),
        beforeSend: function () {
            $("#ajax-loader").show();
        },
        complete: function () {
            $("#ajax-loader").hide();
        },
        error: searchFailed,
        success: function (data) {
            var html = Mustache.to_html($("#artistTemplate").html(),
            { artists: data });
            $("#searchresults").empty().append(html);
        }
    });
});

The to_html method of Mustache combines the template with the JSON data to produce markup. The code takes the template output and places the output in the search results element.

Thank you,
Keep It Simple and Straightforward 🙂
~:H}{H:~

Advertisements

MVC 4.0: Autocomplete with jQuery UI

mvc

Autocomplete is a very nice feature that makes your website more responsive to user inputs. I like this feature very much 🙂

Before you use autocomplete, you can set up the application to include the base theme style sheet by adding it to the layout view:


<link href="~/Content/themes/base/jquery-ui.css" rel="stylesheet" type="text/css" />

You’ll need to fi nd the input element from JavaScript and attach the jQuery autocomplete behavior. One approach to do this is to borrow an idea from the MVC framework and use a data dash attribute:


<input type="text" name="q" data-autocomplete-source="@Url.Action("QuickSearch", "Home")" />

Note that you’ve pointed the source to a controller action.

You can use the following code during the ready event to attach autocomplete to all inputs with the data-autocomplete-source attribute:


$("input[data-autocomplete-source]").each(function () {
var target = $(this);
target.autocomplete({ source: target.attr("data-autocomplete-source") });
});

Autocomplete expects to call a data source and receive a collection of objects it can use to build a list for the user. The QuickSearch action of the HomeController needs to return data in a format autocomplete will understand.

Autocomplete expects to call a data source and receive objects in JSON format. Fortunately, it’s easy to generate JSON from an MVC controller action, as you’ll see soon. The objects must have a property called label, or a property called value, or both a label and a value. Autocomplete uses the label property in the text it shows the user. When the user selects an item from the autocomplete list, the widget will place the value of the selected item into the associated input. If you don’t provide a label, or don’t provide a value, autocomplete will use whichever property is available as both the value and the label.

To return the proper JSON, you’ll implement QuickSearch with the following code:


public ActionResult QuickSearch(string term)
{
var artists = GetArtists(term).Select(a => new {value = a.Name});
return Json(artists, JsonRequestBehavior.AllowGet);
}
private List<Artist> GetArtists(string searchString)
{
return storeDB.Artists
.Where(a => a.Name.Contains(searchString))
.ToList();
}

You are ready to run your application now, you should have something like the following:

autocomplete

Thank you 🙂

Keep It Simple & Straightforward.

~:H}{H:~

 

MVC 4.0: How to support client-side custom validation

mvcI talked about CUSTOM VALIDATION LOGIC  and we saw how to implement custom validation logic to our model classes.

Here we will see how to support client-side validation for our custom validation attribute.

I will start with the solution then I will explain it in details.

    public class MaxWordsAttribute : ValidationAttribute,
                                     IClientValidatable
    {
        public MaxWordsAttribute(int wordCount)
            :base("Too many words in {0}")
        {
            WordCount = wordCount;
        }

        public int WordCount { get; set; }

        protected override ValidationResult IsValid(
            object value,
            ValidationContext validationContext)
        {
            if (value != null)
            {
                var wordCount = value.ToString().Split(' ').Length;
                if (wordCount > WordCount)
                {
                    return new ValidationResult(
                        FormatErrorMessage(validationContext.DisplayName)
                    );
                }
            }
            return ValidationResult.Success;
        }

        public IEnumerable<ModelClientValidationRule> GetClientValidationRules(
            ModelMetadata metadata, ControllerContext context)
        {
            var rule = new ModelClientValidationRule();
            rule.ErrorMessage = FormatErrorMessage(metadata.GetDisplayName());
            rule.ValidationParameters.Add("wordcount", WordCount);
            rule.ValidationType = "maxwords";
            yield return rule;
        }
    }

To support client-side validation, you need your attribute to implement an interface.
The IClientValidatable interface defines a single method: GetClientValidationRules.
When the MVC framework finds a validation object with this interface present, it invokes GetClientValidationRules to retrieve — you guessed it — a sequence of ModelClientValidationRule objects. These objects carry the metadata, or the rules, the framework sends to the client.

If you think about the scenario, there are a few pieces of information you’d need on the client to run the validation:

  • What error message to display if the validation fails (Error Message)
  • How many words are allowed (Validation Parameters)
  • An identifi er for a piece of JavaScript code that can count the words (ValidationType)

The MVC framework takes the rules given back from the GetClientValidationRules method and serializes the information into data dash attributes on the client:

<input
data-val="true"
data-val-length="The field Title must be a string with a maximum length of 160."
data-val-length-max="160"

data-val-maxwords="Too many words in Title"
data-val-maxwords-wordcount="10"

data-val-required="An Album Title is required" id="Title" name="Title"
type="text" value="For Those About To Rock We Salute You" />

Now you have metadata on the client, but you still need to write some script code to execute the validation logic.

You’ll need two pieces of script in place for validation to work:

  • The adapter: The adapter works with the unobtrusive MVC extensions to identify the required metadata. The unobtrusive extensions then take care of retrieving the values from data dash attributes and adapting the data to a format jQuery validation can understand.
  • The validation rule itself: This is called a validator in jQuery parlance.
/// <reference path="jquery-1.6.2.js" />
/// <reference path="jquery.validate.js" />
/// <reference path="jquery.validate.unobtrusive.js" />

if ($.validator && $.validator.unobtrusive) {

    $.validator.unobtrusive.adapters.addSingleVal("maxwords", "wordcount");

    $.validator.addMethod("maxwords", function (value, element, maxwords) {
        if (value) {
            if (value.split(' ').length > maxwords) {
                return false;
            }
        }
        return true;
    });

}

You have four kinds of Adapter Methods (addBool, addSingleValue, addMinMax, add). For the maximum words scenario, you could use either addSingleVal or addMinMax (or add, because it can do anything).

The validator method takes two parameters:

  • The name of the validator, which by convention matches the name of the adapter (which matches the ValidationType property on the server).
  • A function to invoke when validation occurs.

The validator function accepts three parameters and can return true (validation passed) or false (validation failed):

  • The fi rst parameter to the function will contain the input value (like the title of an album).
  • The second parameter is the input element containing the value to validate (in case the value itself doesn’t provide enough information).
  • The third parameter will contain all the validation parameters in an array, or in this case, the single validation parameter (the maximum number of words).

Thank You,
Keep It Simple & Straightforward 🙂
~:H}{H:~

MVC 4.0: CUSTOM VALIDATION LOGIC

The extensibility of the ASP.NET MVC framework means an infinite number of possibilities exist for implementing custom validation logic. However, this post focuses on two core scenarios:

  • Packaging validation logic into a custom data annotation
  • Packaging validation logic into a model object itself

Custom Annotations

Imagine you want to restrict the last name value of a customer to a limited number of words. For example, you might say that 10 words are too many for a last name.

    public class MaxWordsAttribute : ValidationAttribute
    {
        public MaxWordsAttribute(int maxWords)
            :base("{0} has too many words.")
        {
            _maxWords = maxWords;
        }

        protected override ValidationResult IsValid(
            object value, ValidationContext validationContext)
        {
            if (value != null)
            {
                var valueAsString = value.ToString();
                if (valueAsString.Split(' ').Length > _maxWords)
                {
                    var errorMessage = FormatErrorMessage(validationContext.DisplayName);
                    return new ValidationResult(errorMessage);
                }
            }
            return ValidationResult.Success;
        }

        private readonly int _maxWords;
    }

With the validation logic in place, you can apply the attribute to any model property:

    [Required]
    [StringLength(160)]
    [MaxWords(10, ErrorMessage="There are too many words in {0}")]
    public string LastName { get; set; }

Now if the customer types in too many words, he’ll see the message in the following figure in the view.
maxwords validation

IValidatableObject

A self-validating model is a model object that knows how to validate itself. A model object can announce this capability by implementing the IValidatableObject interface.

    public class Order : IValidatableObject
    {
        public IEnumerable<ValidationResult> Validate(
        ValidationContext validationContext)
        {
            if (LastName != null &&
            LastName.Split(' ').Length > 10)
            {
                yield return new ValidationResult("The last name has too many words!",
                new[] { "LastName" });
            }
        }
        // rest of Order implementation and properties
        // ...
    }

This has a few notable differences from the attribute version.

  • The method the MVC run time calls to perform validation is named Validate instead of IsValid, but more important, the return type and parameters are different.
  • The return type for Validate is an IEnumerable instead of a single ValidationResult, because the logic inside is ostensibly validating the entire model and might need to return more than a single validation error.
  • There is no value parameter passed to Validate because you are inside an instance method of the model and can refer to the property values directly.

Many validation scenarios are easier to implement using the IValidatableObject approach, particularly scenarios where the code needs to compare multiple properties on the model to make a validation decision.

Thank you,
Keep It Simple and Straightforward 🙂
~:H}{H:~

MVC 4.0: Normal vs. Ajax Search

I’m reading now Wrox Professional ASP.NET MVC 4.0, this book is really useful that I recommend to read if you want to learn MVC 4.0 and be professional.

Suppose that we have a Home Controller, in the Index View there will be a text box to enter text to search for and a button to view the search result.

We can do that by two ways:

  1. Normal Search
  2. Ajax Search

1. Normal Search

In the Index view we will Create a Form with Text box and a Search button.

@using (Html.BeginForm("Search", "Home", FormMethod.Get))
{

    <h3>Search Music Albums:</h3>
    <input type="text" name="q" />
    <input type="submit" value="Search" />
}

We can use the Html helper method to create the form, When you press the Search button the model binder will pass the text you entered as q parameter to the Search action method located in the Home controller.

        private MusicStoreDBContext storeDb = new MusicStoreDBContext();

        public ActionResult Search(string q)
        {
            var albums = storeDb.Albums.Include("Artist").Where(a => a.Title.Contains(q)).Take(10);
            return this.View(albums);
        }

The Search action method will search our Music Store and get all albums with Title contains the search text and display the result in a new view called Search.

@model IEnumerable<MvcMusicStore.Models.Album>

@{
    ViewBag.Title = "Search";
}

<h2>Search Result</h2>

<table>
    <tr>

        <th>
           Artist
        </th>
        <th>
            Title
        </th>
        <th>
            Price
        </th>

        <th></th>
    </tr>

@foreach (var item in Model) {
    <tr>

        <td>
           @item.Artist.Name
        </td>
        <td>
            @item.Title
        </td>
        <td>
            @string.Format("{0:c}", item.Price)
        </td>

    </tr>
}

</table>

In the previous scenario you enter the search text in one view and the result appears in another one.
What if we want to display the search result in the same view??

2. Ajax Search

 

In the Index view we will use the Ajax helper to create Ajax Form.

@using (Ajax.BeginForm("AjaxSearch", "Home",
    new AjaxOptions { HttpMethod = "GET", InsertionMode = InsertionMode.Replace, UpdateTargetId = "searchResults" }))
{
    <h3>Search Music Albums:</h3>
    <input type="text" name="q" />
    <input type="submit" value="Ajax Search" />
}

<table id="searchResults">

</table>

Note the AjaxOptions parameter with InsertionMode and UpdateTargetId fields.

Lets have a look at AjaxSearch action method.

        private MusicStoreDBContext storeDb = new MusicStoreDBContext();

        public PartialViewResult AjaxSearch(string q)
        {
            var albums = storeDb.Albums.Include("Artist").Where(a => a.Title.Contains(q)).Take(10);
            return this.PartialView(albums);
        }

The Ajax Search uses PartialViewResult to build the search result and place it in the Index view.
The Ajax Search Partial View will only contains a table for listing the search result.

@model IEnumerable<MvcMusicStore.Models.Album>

<table>
    <tr>

        <th>
           Artist
        </th>
        <th>
            Title
        </th>
        <th>
            Price
        </th>

        <th></th>
    </tr>

@foreach (var item in Model) {
    <tr>

        <td>
           @item.Artist.Name
        </td>
        <td>
            @item.Title
        </td>
        <td>
            @string.Format("{0:c}", item.Price)
        </td>

    </tr>
}

</table>

Don’t forget to add reference to JQuery Unobtrusive Ajax script file in the _Layout.cshtml.


<script src="~/Scripts/jquery.unobtrusive-ajax.min.js" type="text/javascript"></script>

When you run your application you should have something like that:

search

Thank you and Keep It Simple and Straightforward 🙂
~:H}{H:~