Posts
201
Comments
1111
Trackbacks
51
June 2008 Entries
MVC JSON - JsonResult and jQuery

The latest release of the MVC framework provides the JsonResult for Controller actions.  I was surprised that I did not find a weatlh of examples for usage so I figured it shouldn't be too hard to get a decent example going.  It turns out, it was even easier than I anticipated.  I wanted to create an example where I would invoke an AJAX call to dynamically populate a dropdown list. 

jQuery has recently received a huge surge of interest of the ASP.NET MVC community so I've been getting up to speed on it myself.  I am quickly turning into a true believer and I've been impressed not only with how few lines of javascript it takes me to get things done but also how (relatively) easy it was to learn.  In addition to numerous examples on the jQuery web site I also recommend jQuery In Action.

Getting an AJAX request with jQuery and JSON in MVC is actually pretty simple.  The first step is to have a Controller action that returns a JsonResult:

   1:  public JsonResult GetStateList()
   2:  {
   3:      List<ListItem> list = new List<ListItem>() {
   4:          new ListItem() { Value = "1", Text = "VA" },
   5:          new ListItem() { Value = "2", Text = "MD" },
   6:          new ListItem() { Value = "3", Text = "DC" }
   7:      };
   8:      return this.Json(list);
   9:  }

Here I'm using the System.Web.Mvc.ListItem class (which is used as items in the SelectList) to simply return a contrived list of US states to populate a dropdown list.  All I have to do is call the Json() method passing in my object and it will automatically be serialized to JSON.  When the request is made the AJAX call can be seen in the Web Development Helper which is an invaluable tool to any AJAX development:

Notice the "application/json" in the request header and the textual representation of the JSON that is being returned from my JSON controller action.

The jQuery to make this happen is quite succinct:

   1:  <script type="text/javascript">
   2:  $(function() {
   3:      $('#btnFillList').click(function() {
   4:          $.getJSON("/Home/GetStateList", null, function(data) {
   5:              $("#stateList").fillSelect(data);
   6:          });
   7:       });
   8:       
   9:       $('#btnClearList').click(function() {
  10:          $("#stateList").clearSelect();
  11:       });
  12:  });
  13:  </script>

Notice on line #4 the use of the $.getJSON() jQuery method.  Then I've simply written a re-usable jQuery method called fillSelect() which will work on the JSON format returned by the ListItem class returned by my controller action:

   1:  $.fn.clearSelect = function() {
   2:      return this.each(function() {
   3:          if (this.tagName == 'SELECT')
   4:              this.options.length = 0;
   5:      });
   6:   } 
   7:   
   8:  $.fn.fillSelect = function(data) {
   9:      return this.clearSelect().each(function() {
  10:          if (this.tagName == 'SELECT') {
  11:              var dropdownList = this;
  12:              $.each(data, function(index, optionData) {
  13:                  var option = new Option(optionData.Text, optionData.Value);
  14:                  
  15:                  if ($.browser.msie) {
  16:                      dropdownList.add(option);
  17:                  }
  18:                  else {
  19:                      dropdownList.add(option, null);
  20:                  }
  21:              });
  22:          }
  23:      });
  24:   }

On line #13 above you'll see the strongly-typed "Text" and "Value" properties that I have the luxury of using because of the native JSON serialization into objects.

The complete code sample can be downloaded here.  It's barely 50 lines of code in all.

Posted On Saturday, June 28, 2008 7:35 AM | Comments (39)
MVC Control Extensions - Automatically Set Control Names

To set a normal text box in the MVC framework, the most typical code would look like this:

   1:  <%=Html.TextBox("FirstName", ViewData.Model.Contact.FirstName)%>

The key here is that you should set the name of the textbox to be the exact same name of the property name.  If you do this, then it enables you to use extension methods such as the UpdateFrom() method to automatically populate your object from Request.Form parameters when you post to your controller action.  This is all well and good but it is very easy to make a typo when typing the "FirstName" string for example.  Wouldn't it be nice if this all just happened automatically? 

Well, I found a very elegant solution to this last week when I was browsing the latest code of the Validation Framework on CodePlex.  In the MVC quick start code for the Validation Framework, they provide extension methods for creating a "ValidatedTextbox", for example, that works with that validation framework which looks like this:

   1:  <%=Html.TextBoxValidated(() => ViewData.Model.Product.ProductName)%>

Not only is this solution elegant but I quickly realized that this could be easily generalized to so many other scenarios.  Not just in the context of this validation framework but also for creating the most "normal" textbox scenario.  By using this method, I can now re-write the first example like this:

   1:  <%=Html.TextBox(() => ViewData.Model.Contact.FirstName)%>

The resulting HTML that is produced will populate the textbox with the value from the FirstName property and it will also name the textbox "FirstName".  So the HTML the gets rendered in the browser is:

   1:  <input type="text" name="FirstName" id="FirstName" value="Bill" />

It turns out that there is not many lines of code required to pull this off.  The lambda expression is specified in the mark up and a Linq Expression is used for the method parameter.  The complete code for the Textbox extension methods is as follows:

   1:  public static class ControlExtensions
   2:  {
   3:      public static string TextBox<T>(this HtmlHelper htmlHelper, Expression<Func<T>> expression)
   4:      {
   5:          return TextBox<T>(htmlHelper, expression, null);
   6:      }
   7:   
   8:      public static string TextBox<T>(this HtmlHelper htmlHelper, Expression<Func<T>> expression, object htmlAttributes)
   9:      {
  10:          MemberExpression memberExpression = expression.Body as MemberExpression;
  11:          if (memberExpression == null)
  12:          {
  13:              throw new ArgumentException("The specified expression is not a valid MemberExpression.", "expression");
  14:          }
  15:   
  16:          Func<T> compile = expression.Compile();
  17:          T value = compile.Invoke();
  18:   
  19:          return htmlHelper.TextBox(memberExpression.Member.Name, Convert.ToString(value), htmlAttributes);
  20:      }
  21:  }

Notice that since this is a MemberExpression where we're just getting a value from a property, we can simply use the Member.Name to get at the "FirstName" string.  And then we can use the Compile() method of the Expression<TDelegate> class to get executable code in the form of a delegate that represents the lambda.  Now that we have the Func<T> we can simply call Invoke() to get at the value of the FirstName property.  Now that we have the name and value, we can pass it to the already existing TextBox extension methods.

This technique can really be generalized to any MVC control.  It can also be generalized to create composite controls as they have done in the Validation Framework above.  In my opinion, Microsoft should consider including this type of thing in the base MVC library.  But if not, it's easy enough to add to your own MVC helper libraries.

Posted On Sunday, June 22, 2008 11:03 PM | Comments (4)
Leverage EntLib Validation Application Block in MVC

UPDATE: This post is now out of date as of the CTP5 release of the MVC framework. For the lastest version, see this post here.


One of the areas of MVC that is still an open area in development is how best to handle UI validations.  There are numerous approaches currently being examined.

The Enterprise Library's VAB is great because it allows you to specify all your validation logic in your business layer where it belongs rather than in your UI.  Additionally, it comes with the PropertyProxyValidator to automatically display validation messages in the UI and avoid having to put hundreds of validators on a page.  However, the PropertyProxyValidator is a traditional ASP.NET validator control and does not work in the MVC framework.

The good news here is that duplicating the functionality of the PropertyProxyValidator in the MVC framework is not difficult.  Currently, the MVC framework comes with some built-in extension methods to enable the developer to easy create UI elements hanging off the HtmlHelper property.  I've written a couple of similar extension methods following the same pattern that essentially produce the same output as the ProperyProxyValidator.  This enables us to write UI code without cluttering our mark up with a ton of validator controls:

   1:  <%=Html.TextBox("FirstName", ViewData.Model.Contact.FirstName)%>
   2:  <%=Html.Validator("FirstName", ViewData.Model.ValidationResults)%>

Notice that a property called "ValidationResults" is being passed in to this Validator.  When the form is submitted, it goes to a controller action (let's say "Save" for this example):

   1:  public ActionResult Save(int id)
   2:  {
   3:      Contact contact = new Contact();
   4:      contact.ContactID = id;
   5:      BindingHelperExtensions.UpdateFrom(contact, Request.Form);
   6:   
   7:      ValidationResults validationResults = Validation.Validate(contact);
   8:      if (validationResults.IsValid)
   9:      {
  10:          contactManager.SaveContact(contact);
  11:          return RedirectToAction(Views.Index);
  12:      }
  13:      else
  14:      {
  15:          ContactViewData contactViewData = new ContactViewData(contact);
  16:          contactViewData.ValidationResults = validationResults;
  17:          ViewData.Model = contactViewData;
  18:          return View(Views.EditContact);
  19:      }
  20:  

So if the contact object in this example is valid, we save it to the database and proceed as normal to the next redirect action.  However, if the contact is not valid, we redisplay the edit contact page again and this time we pass the ValidationResults along with the existing contact object in the Model to the View.  This Validator control will grab the appropriate validation messages for the specified control/property and display any validation messages for that specific control.  The following is the complete code for the Validator extension methods:

   1:  public static class ValidationControlExtensions
   2:  {
   3:   
   4:      public static string Validator(this HtmlHelper html, string controlToValidate, ValidationResults validationResults)
   5:      {
   6:          return Validator(html, controlToValidate, validationResults, new { style="color:Red;" });
   7:      }
   8:   
   9:      public static string Validator(this HtmlHelper html, string controlToValidate, ValidationResults validationResults, object htmlAttributes)
  10:      {
  11:          return Validator(html, controlToValidate, controlToValidate, validationResults, htmlAttributes);
  12:      }
  13:   
  14:      public static string Validator(this HtmlHelper html, string controlToValidate, string propertyName, ValidationResults validationResults, object htmlAttributes)
  15:      {
  16:          if (validationResults == null)
  17:          {
  18:              return null;
  19:          }
  20:          
  21:          var filteredResults = validationResults.Where(r => r.Key == propertyName);
  22:          if (filteredResults.Count() == 0)
  23:          {
  24:              return null;
  25:          }
  26:   
  27:          StringBuilder errorMessage = new StringBuilder();
  28:          foreach (ValidationResult validationResult in filteredResults)
  29:          {
  30:              errorMessage.Append(validationResult.Message);
  31:              errorMessage.Append("<br/>");
  32:          }
  33:   
  34:          TagBuilder builder = new TagBuilder("span");
  35:          IDictionary<string, object> attribueDictionary = new RouteValueDictionary(htmlAttributes);
  36:          builder.Attributes = ToStringDictionary(attribueDictionary);
  37:          builder.InnerHtml = errorMessage.ToString();
  38:          return builder.ToString();
  39:      }
  40:   
  41:      /// <summary>
  42:      /// ToStringDictionary is Internal to System.Web.Mvc.HtmlHelper
  43:      /// </summary>
  44:      /// <typeparam name="TKey"></typeparam>
  45:      /// <typeparam name="TValue"></typeparam>
  46:      /// <param name="dictionary"></param>
  47:      /// <returns></returns>
  48:      internal static Dictionary<string, string> ToStringDictionary<TKey, TValue>(IDictionary<TKey, TValue> dictionary)
  49:      {
  50:          if (dictionary == null)
  51:          {
  52:              return new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
  53:          }
  54:          return dictionary.ToDictionary<KeyValuePair<TKey, TValue>, string, string>(entry => Convert.ToString(entry.Key, CultureInfo.InvariantCulture), entry => Convert.ToString(entry.Value, CultureInfo.InvariantCulture), StringComparer.OrdinalIgnoreCase);
  55:      }
  56:  }

One note here is that you can see I'm using a class called TagBuilder inside my method.  This is actually an internal class in the System.Web.Mvc assembly.  I just cracked open Reflector and did a complete copy/paste of that code into my own assembly so that I could leverage it.  But what would be really nice is if Microsoft would change the accessibility on that class to public to more easily enable scenarios like this when people want to build their own control extension methods.

Posted On Tuesday, June 17, 2008 3:47 PM | Comments (3)
EntLib Validation Application Block - Subclassing PropertyProxyValidator for ValueConvert

The Enterprise Library VAB provides the PropertyProxyValidator for ASP.NET applications so that you can attach it to a single control and it will display any validation messages for that business object property.  This automatic UI validation is great because it avoids duplicating your business layer validation logic in your UI.  Additionally, you can just use ONE validator control whereas using traditional ASP.NET validation controls you might need to attach 3-4 validation controls to a single UI element (e.g., RequireFieldValidator, RangeValidator, RegexValidator, etc.) which can clutter your aspx pages quickly.

However, one of the gotchas with working with the PropertyProxyValidator control is that, while it is quite elegant for strings, you have to do a little extra work for data types such as DateTime.  Specifically, if you are working with a DateTime, you have to attach a ValueConvert method to your PropertyProxyValidator control in which you perform the data type conversion.  The following code snippet is directly out of the VAB documentation:

   1:  protected void dateOfBirthValidator_ValueConvert(object sender, ValueConvertEventArgs e)
   2:  {
   3:          string value = e.ValueToConvert as string;
   4:          try
   5:          {
   6:              e.ConvertedValue = DateTime.Parse(value, System.Globalization.CultureInfo.CurrentCulture);
   7:          }
   8:          catch
   9:          {
  10:              e.ConversionErrorMessage = "Date Of Birth is not in the correct format.";
  11:              e.ConvertedValue = null;
  12:          }
  13:  }

This extensibility is great but if I need to perform multiple DateTime validations like this throughout my application, I don't want to have to copy/paste this code everywhere.  Even if I encapsulate it in a static utility method, I still have to go through the pain of wiring up an event for any control where I need this just to make that 1-line call to the utility method.

So...a better approach is to simply subclass the PropertyProxyValidator control.  In the following example, I just wire up the conversion in the contructor.  This enables me to avoid having to wire up the event in the aspx code and and also enables me to avoid having to include the event in the code behind.  I just specify the DateTimePropertyProxyValidator in my aspx code the normal way:

   1:  <asp:TextBox id="txtDateOfBirth" runat="server" />
   2:  <vab:DateTimePropertyProxyValidator ID="dobVal" runat="server" ControlToValidate="txtDateOfBirth" PropertyName="DateOfBirth" SourceTypeName="Person"/>

The same pattern could obviously be used for other data types.  Below is the complete code for the DateTimePropertyProxyValidator:

   1:  public class DateTimePropertyProxyValidator : PropertyProxyValidator
   2:  {
   3:      public DateTimePropertyProxyValidator()
   4:      {
   5:          this.ValueConvert += OnValueConvert;
   6:      }
   7:   
   8:      private void OnValueConvert(object sender, ValueConvertEventArgs e)
   9:      {
  10:          DateTime result;
  11:   
  12:          if (DateTime.TryParse((string)e.ValueToConvert, out result))
  13:          {
  14:              e.ConvertedValue = result;
  15:          }
  16:          else
  17:          {
  18:              e.ConversionErrorMessage = "Date is not in the correct format.";
  19:              e.ConvertedValue = null;
  20:          }
  21:      }
  22:  }
Posted On Monday, June 16, 2008 3:19 PM | Comments (1)
EntLib Validation Application Block - Required String Validator

In a previous post I discussed validating strings with the VAB in the context of ASP.NET applications.  In summary, you must have a NotNullValidator and also a StringLengthValidator that prevents a string length of zero on the lower bound and prevents a string length above your upper bound (e.g., 50 characters).  That attribute might look like this:

   1:  [StringLengthValidator(1, 50, MessageTemplate="First Name must be 1-50 characters.")] 

But the problem is that the StringLengthValidator is really doing 2 jobs - one to check if the user entered a value and another to check the upper bound. Although including the NotNullValidator complete and correct, it doesn't do much in the context of asp.net applications which default to an empty string. 

So from a user perspective, it would be nice to have one message if the user leaves it blank and another message if they've exceed the upper bound (or didn't follow an appropriate Regex pattern, etc., etc.).  In other words, it would be nice to have a RequiredStringValidator so we could write our business object like this (notice we no longer have to care about the lower bound on the string length validator):

   1:  public class Person
   2:  {
   3:      [RequiredStringValidator(MessageTemplate = "First Name is required.")]
   4:      [StringLengthValidator(50, MessageTemplate = "First Name must be less than 50 characters.")]
   5:      public string FirstName { get; set; }
   6:  

Also, specifiying a MessageTemplate on the RequiredStringValidator is optional.

So creating your own custom validator to do this is actually quite easy.  The complete implementation follows:

   1:  public class RequiredStringValidatorAttribute : ValueValidatorAttribute
   2:  {
   3:      protected override Validator DoCreateValidator(Type targetType)
   4:      {
   5:          return new RequiredStringValidator(this.MessageTemplate, this.Negated);
   6:      }
   7:  }
   8:   
   9:  public class RequiredStringValidator : ValueValidator<string>
  10:  {
  11:      public RequiredStringValidator(string messageTemplate, bool negated) : base(messageTemplate, null, negated)
  12:      {
  13:      }
  14:      
  15:      protected override void DoValidate(string objectToValidate, object currentTarget, string key, ValidationResults validationResults)
  16:      {
  17:          if (string.IsNullOrEmpty(objectToValidate) != Negated)
  18:          {
  19:              LogValidationResult(validationResults, GetMessage(objectToValidate, key), currentTarget, key);
  20:          }
  21:      }
  22:   
  23:      protected override string DefaultNegatedMessageTemplate
  24:      {
  25:          get
  26:          {
  27:              return "Field cannot have a value.";
  28:          }
  29:      }
  30:   
  31:      protected override string DefaultNonNegatedMessageTemplate
  32:      {
  33:          get
  34:          {
  35:              return "Field is required.";
  36:          }
  37:      }
  38:  

I've supported Negations for completeness but I can't really think of a real-world example where you'd want to use it in this context. 

So overall, the RequiredStringValidator check for both null and empty string.  Thus, you don't have to feel like you're kludging the StringLengthValidator into doing two different jobs.

Posted On Thursday, June 12, 2008 11:23 PM | Comments (10)
EntLib Validation Application Block - Validate a string in ASP.NET

You've created a class like this and all your unit tests pass just fine when you check for null strings or strings greater than 50 characters:

   1:  public class Person
   2:  {
   3:      [NotNullValidator(MessageTemplate="First Name is required.")]
   4:      [StringLengthValidator(50, MessageTemplate="First Name must be 1-50 characters.")]
   5:      public string FirstName { get; set; }
   6:  }

But then when you run it in an ASP.NET application with the PropertyProxyValidator validation control provided by VAB, your business object suddenly shows as valid even though the user left the text box blank.

   1:  <asp:TextBox ID="txtFirstName" runat="server" />
   2:  <vab:PropertyProxyValidator id="firstNameVal" runat="server" ControlToValidate="txtFirstName" 
                                   PropertyName="FirstName" SourceTypeName="WebApplication1.Person"/>

Why is this happening?  The reason is because the ASP.NET infrasture will put an empty string (i.e., a zero-length string) in that property rather than a null.  In point of fact, the validator was not set up correctly to begin with.  The intent is also to prevent an empty string.  So to correct the implementation of the StringLengthValidator, you must give the lower bound of 1 as the first argument as shown here:

   1:  [StringLengthValidator(1, 50, MessageTemplate="First Name must be 1-50 characters.")] 

Additionally, the VAB will allow you to specify whether you want the number specified for the lower/upper bound to be considered inclusvie/exclusive for the range.

Posted On Tuesday, June 10, 2008 9:27 PM | Comments (3)

View Steve Michelotti's profile on LinkedIn

profile for Steve Michelotti at Stack Overflow, Q&A for professional and enthusiast programmers




Google My Blog

Tag Cloud