Html.RatingFor: Extending the MVC HtmlHelper

When working on a web application, I was in the need to add a rating for a product. That rating will be between 1 and 5 and will be always an int. So my model has a property like public int Rating {get;set;}. I decided to add 5 radio buttons, and each will hold the corresponding rating value.

But then (as always happen) the requirement changed. We didn’t want to have only 1 rating property, but 5. So adding 5 radios for each was something that I didn’t want to happen

In order to solve this problem, I created an extension method for the HtmlHelper class that we normally use in our MVC applications. As you may notice, in the method I created all the logic for adding the set of radio buttons needed for the rating process.


public static MvcHtmlString RatingFor<tmodel, TProperty>(this HtmlHelper htmlHelper, Expression<func<tmodel, TProperty>> expression, int from, int to, object htmlAttributes = null)
	{
		var builder = new StringBuilder();
 
		var metadata = ModelMetadata.FromLambdaExpression(expression, htmlHelper.ViewData);
 
		var model = metadata.Model;
		var name = ExpressionHelper.GetExpressionText(expression);
 
		var attributes = HtmlHelper.AnonymousObjectToHtmlAttributes(htmlAttributes);
 
		var fullName = htmlHelper.ViewContext.ViewData.TemplateInfo.GetFullHtmlFieldName(name);
 
		int direction = 1;
		if (from > to)
			direction = -1;
 
		for (var i = from; direction == 1 ? i <= to : i >= to; i += direction)
		{
			var tagBuilder = new TagBuilder("input");
			tagBuilder.MergeAttributes(attributes);
			tagBuilder.MergeAttribute("type", "radio");
			tagBuilder.MergeAttribute("name", fullName, true);
			tagBuilder.MergeAttribute("value", i.ToString(CultureInfo.InvariantCulture));
			//If model has a value we need to select it
			if (model != null && model.Equals(i))
			{
				tagBuilder.MergeAttribute("checked", "checked");
			}
			tagBuilder.GenerateId(fullName);
 
 
			ModelState modelState;
			if (htmlHelper.ViewData.ModelState.TryGetValue(fullName, out modelState))
			{
				if (modelState.Errors.Count > 0)
				{
					tagBuilder.AddCssClass(HtmlHelper.ValidationInputCssClassName);
				}
			}
 
			tagBuilder.MergeAttributes(htmlHelper.GetUnobtrusiveValidationAttributes(name, metadata));
 
			builder.AppendLine(tagBuilder.ToString(TagRenderMode.SelfClosing));
		}
 
 
		return MvcHtmlString.Create(builder.ToString());
	}

One important part of this code is

if (model != null && model.Equals(i))
{
 tagBuilder.MergeAttribute("checked", "checked");
}

where we assign the value of the property if it is already set. This is useful when you use this method on an Edit process.

Now on your view, instead of having to create all that radio buttons manually, you can have something like this

@Html.RatingFor(model => model.Rating, 1, 5)

in order to add a rating from 1 to 5.

Hopefully you will find this useful. If you have created another useful helper, it would be nice if you share it with the community 🙂

Public field not bound when posting to WebAPI (or a deep dive into WebAPI model binding)

When trying to create a sandbox project using WebAPI (on MVC4), I was struggling with a weird problem: My data wasn’t being received in the server. I had the following jQuery call

$.post("api/Values", {value1:1, value2:2}, function(result){ console.log(result); })

and the WebAPI service action that I was targeting was something like this

public IEnumerable Post(Dummy value)
{
 return new string[] { value.Value1, value.Value2 };
}

I noticed that even that the instance of Dummy was being created, Value1 and Value2 where always null. The Dummy class was

public class Dummy
{
 public string Value1;
 public string Value2;
}

Pretty simple, right?. Well, after reading doing a lot of research, I changed by accident one of the Dummy fields to become a property

public class Dummy
{
 public string Value1;
 public string Value2 {get;set;}
}

I tested again and Voilà!!… well, half voilà actually… When posting, now I was receiving data in Value2, but still not in Value1. This was really intriguing… how come property was being assigned correctly but not the field? Both are public, right? Why the difference?
Obviously, I knew the solution was changing both fields to be properties now, but I wanted to know why was that happening. I started digging on how WebAPI works and found a really interesting Web API poster, that describes the full lifecycle of a HTTP message. There I got my first clue, so I started researching on how ModelBinding happens. As described there, one of the binding methods is MediaTypeFormatter. Since I was sending JSON object, I tested the Deserialization process based on the test methods provided in the WebAPI overview site

T Deserialize(MediaTypeFormatter formatter, string str) where T : class
{
 // Write the serialized string to a memory stream.
 Stream stream = new MemoryStream();
 StreamWriter writer = new StreamWriter(stream);
 writer.Write(str);
 writer.Flush();
 stream.Position = 0;
 // Deserialize to an object of type T
 return formatter.ReadFromStreamAsync(typeof(T), stream, null, null).Result as T;
}

passing the same JSON object that I had on my jQuery call. The result: The method assigned successfully the values for both the field and the property. By inspecting the HTTP Request headers, I found out that data wasn’t being actually sent as JSON but in the following format: Content-Type:application/x-www-form-urlencoded; charset=UTF-8, which tells the server that data is being sent like this: Value1=1&Value2=2. Then, we need to change the AJAX call to be like this

$.ajax({
  url: "api/Values",
  data: JSON.stringify({Value1:1,Value2:2}),
  type: "POST",
  contentType:"application/json; charset=utf-8"
})

please notice 2 things: I changed the contentType for the request AND Stringified the JSON object. By doing these changes, Dummy public fields were now populated correctly.
Now, I still wanted to know why my values weren’t bound when I wasn’t specifying the request content type. Doing more research, I found this really interesting article by Mike Stall called How WebAPI does parameter binding which states

There are 2 techniques for binding parameters: Model Binding and Formatters. In practice, WebAPI uses model binding to read from the query string and Formatters to read from the body

If you are not yet bored, you might remember that when we didn’t specify the request content type, the data was being sent as Content-Type:application/x-www-form-urlencoded; charset=UTF-8. This means, that WebAPI was using ModelBinding (and not formatters) to populate the Dummy instance. Moreover, the article has another interesting declaration:

ModelBinding is the same concept as in MVC, […]. Basically, there are “ValueProviders” which supply pieces of data such as query string parameters, and then a model binder assembles those pieces into an object.

And how does ModelBinding work in MVC? That was my next question. And I was really happy that Microsoft open-sourced the ASP.Net WebStack, because there is where we can find the answer. If we look into DefaultModelBinder source code, we’ll find that when talking about complex models, it only looks for the object properties to populate the data (maybe because having public fields is a bad practice).
Well, I hope you can find this post as interesting as I found learning all this. Some times making silly errors can drive you into learn really interesting things.

Useful references

Localize your MVC app based on a subdomain

Having an application in multiple languages is now a requirement in many projects. In ASP.net, you can tell your application that the language that should be using corresponds to the one the browser is specifying. While this is a really nice feature in the ideal scenarios (since the user gets the applications in the proper language automatically), there are some scenarios where this might be not the expected behavior like:

  • If your user’s computer locale is different than the one he or she prefers for using your application (like when he or she is using a different computer than his/her own)
  • When the browser settings have been modified to some value diferent than whatever the user prefers and he or she does not have the knowledge to adjust this setting on the browser.
In these cases, the user would rather to have a “fallback” mechanism so he or she can select his/her preferred language. One of the options you can use to achieve this is selecting the language/locale based on a subdomain. By this, you will give the users the following options:
Desired language URL address
English en.myapp.com
Spanish sp.myapp.com
Finnish fi.myapp.com

 

In order to support this, you will need to create an ActionFilterAttribute, something like this

public class LocalizationFilterAttribute : ActionFilterAttribute
    {
        public override void OnActionExecuting(ActionExecutingContext filterContext)
        {
            var locales = new Dictionary();

            locales.Add("mx", "es-MX");
            locales.Add("sp", "es-ES");
            locales.Add("vi", "vi-VN");
            locales.Add("fi", "fi-FI");

            var subdomain = GetSubDomain();

            if (subdomain != string.Empty && locales.ContainsKey(subdomain))
            {
                Thread.CurrentThread.CurrentCulture = new System.Globalization.CultureInfo(locales[subdomain]);
                Thread.CurrentThread.CurrentUICulture = new System.Globalization.CultureInfo(locales[subdomain]);

                HttpContext.Current.Response.Write(String.Format("Culture: {0}", Thread.CurrentThread.CurrentCulture.Name));
            }
            else
            {
                HttpContext.Current.Response.Write("Culture: Default ");
            }
            base.OnActionExecuting(filterContext);
        }
        private string GetSubDomain()
        {
            var url = HttpContext.Current.Request.Headers["HOST"];
            var index = url.IndexOf(".");

            if (index < 0)
            {
                return string.Empty;
            }

            var subdomain = url.Split('.')[0];
            if (subdomain == "www" || subdomain == "localhost")
            {
                return string.Empty;
            }

            return subdomain;
        }
    }

As you may already noticed, with this code you define a list of locales that will be selected according to the provided subdomain. The next step would be registering this filter so it is used in all the views. You can do this in your Global.asax file

public static void RegisterGlobalFilters(GlobalFilterCollection filters)
        {
            filters.Add(new LocalizationFilterAttribute());
            filters.Add(new HandleErrorAttribute());
        }

Once you have a way to set the locale for the current thread, all you need to do is the localization process, which can be done as you already have it. In my case, I’m using resource files to have all the translations and have a fallback resource file if any requested text has no translation on any of the language-specific resource files.

By this, you can provide your users a simple and easy-to-remember way to get your application in their desired language.

Runtime customizable model attributes in ASP.net MVC3

It can be because you want to implement I18N without the use of resources, or your model classes are outside of your MVC project, or any other reason you have that make you want not being tied to the inline (compile time) annotations on your model.
Whatever is the case, you can solve this by creating a custom class that help you to add these annotations dinamically. In this case, will be working on adding some validations defined on an XML file.

Create the CustomValidationProvider class

This CustomValidationProvider class, that will inherit from DataAnnotationsModelValidatorProvider, and it will help you adding all the validations that you need in the view accordingly.
public class CustomValidationProvider{
        private readonly string _validationsFile = "";

        public ConventionModelValidatorProvider(string validationsFile)
        {
            _validationsFile = validationsFile;
        }
}

When creating the instance of ConventionValidatorProvider, it needs to receive the name of the xml that will be used to load the validations. This instance is created in Global.asax file

ModelValidatorProviders.Providers.Clear();
ModelValidatorProviders.Providers.Add(
 new CustomValidationProvider(ConfigurationManager.AppSettings.Get("validationsFile"))
);
It is important to notice that first we clear all the ModelValidatorProviders, in order to remove the one that comes with ASP.NET MVC by default.

Validations XML

As we said earlier, we will define the validations for our models in an xml file. This XML file has the following structure

  
    
      
        
          
 
        
      
 
    
  
 

In this structure we define the models and properties that will be validated. For each defined property one or more validations need to exist; these are the types that we will be using for this example

<validation type="Required" errorMessage="The product description is required" />

The field will be requierd and if not provided, the value on errorMessage attribute will be displayed

<validation type="StringLengthAttribute" min="5" max="10" errorMessage = "5-10 characters" />

The field will be treated as string and its length value needs to be greater or equal than value specified on min attribute and lower or equal to value specified in max atribute, otherwise the value on errorMessage attribute will be displayed

<validation type="RangeAttribute" min="5" max="10" errorMessage = "specify something between 5 and 10" />

The field will be treated as numeric and its value needs to be greater or equal than value specified on min attribute and lower or equal to value specified in max atribute, otherwise the value on errorMessage attribute will be displayed

<validation type="RegularExpressionAttribute" errorMessage = "Code should start with 0x and be followed by only digits or A-F letters">
<regex>
<![CDATA[
[0][x][0-9a-fA-F]+
]]>
</regex>
</validation>

The field will be tested against the specified regular expression; if it does not comply with the regex, the value on errorMessage attribute will be displayed

Adding the validations to the page

Every time a view is loaded, the GetValidators method from the ModelValidator class is called for each propety that we have in the form. By this, all the needed validations are added to the list that will be used by MVC to determine which fields have a specific constraint that needs to be satisfied before saving.
That said, we need then to create our own implementation of the GetValidators method by overriding whatever the base class have right now:
protected override IEnumerable<ModelValidator> GetValidators(ModelMetadata metadata, ControllerContext context, IEnumerable<Attribute> attributes)
Before we go further, there are 4 important elements that we need to identify when this method is executed:

1. Action that it’s being executed

context.Controller.ControllerContext.RouteData.Values["action"].ToString();

2. Controller where this Action exists

context.Controller.ControllerContext.RouteData.Values["controller"].ToString();

3. Property that is being checked if will be validated or not

metadata.PropertyName

4. Model (class) where that property exists

metadata.ContainerType.Name

Once we know this elements, the rest is only read the XML file to determine if the the type and quantity of validations that the current property needs. In order to keep this post as clean as possible, I’ll ommit the code that is used to read the XML file. If you need information on how to do that, you can check this article or have a look on LinqToXML.

We need to filter the XML file that we previously defined to get the validations for the property of the model that is being checked. If at least on validation exists, we start looping on the list and we create the validations using a sort of simple factory. Each validation is created with the counterpart class defined on System.ComponentModel.DataAnnotations Namespace. In this example we’re only defining a few validation types. You can define more if needed.

switch (validationType)
{
 case "Required":
  attr = new RequiredAttribute();
  break;
 case "StringLengthAttribute":
  var attribute1 = validation.Attribute("max");
  var xAttribute2 = validation.Attribute("max");
  if (xAttribute2 != null)
  {
   int max = int.Parse(attribute1 != null && String.IsNullOrEmpty(attribute1.Value)
         ? "0"
         : xAttribute2.Value);
   var attribute2 = validation.Attribute("min");
   var xAttribute3 = validation.Attribute("min");
   if (xAttribute3 != null)
   {
    int min = int.Parse(attribute2 != null && String.IsNullOrEmpty(attribute2.Value)
          ? "0"
          : xAttribute3.Value);

    attr = new StringLengthAttribute(max);
    ((StringLengthAttribute)attr).MinimumLength = min;
   }
  }
  break;
 case "RegularExpressionAttribute":
  var regex = validation.Descendants().Single(a => a.Name == "regex").Value.Trim();
  attr = new RegularExpressionAttribute(regex);
  break;
 case "RangeAttribute":
  var attribute3 = validation.Attribute("max");
  var xAttribute4 = validation.Attribute("max");
  if (xAttribute4 != null)
  {
   double rangeMax = double.Parse(attribute3 != null && String.IsNullOrEmpty(attribute3.Value)
              ? "0"
              : xAttribute4.Value);
   var attribute2 = validation.Attribute("min");
   var xAttribute3 = validation.Attribute("min");
   if (xAttribute3 != null)
   {
    double rangeMin = double.Parse(attribute2 != null && String.IsNullOrEmpty(attribute2.Value)
               ? "0"
               : xAttribute3.Value);
    attr = new RangeAttribute(rangeMin, rangeMax);
   }
  }
  break;
 case "DataTypeAttribute":
  attr = new DataTypeAttribute(String.IsNullOrEmpty(attribute.Value)
           ? ""
           : attribute.Value);
  break;
}

The attribute instance that is created corresponds to the validation that will be performed on screen for that property. Each validation has its own instance, and we store each instance that we create in a list of List<Attribute> type. Once we’re done with the XML reading, the only thing we need to do is to call the base class to do the rest of the work and return the result.

return base.GetValidators(metadata, context, newAttributes);

Wrapping up

As we mentioned at the beginning of the post, using this approach you will gain a lot of flexibility on how you’re adding your model validations; also, you have a reusable way to add them to your model classes no matter where they are located.

Send content to the layout using a Helper on CakePHP

This post is based on Robert Conner’s code forCakePHP 1.x., and I made some changes to get it to work onCakePHP 2.x . The original post can be found here.

Maybe some people have faced problems trying to send some content to the layout on CakePHP. By content I mean not only a simple string but a whole a piece of HTML code. To solve this, we can create a Helper on CakePHP 2.x, according to the following steps:

1. Create the Helper

On theViews/Helpers folder, you need to create the .php file for the helper. In this case we will call it LayoutHelper.php

class LayoutHelper extends AppHelper {
 var $__blockName = null;
 function blockStart($name) {
  if (empty($name))
   trigger_error('LayoutHelper::blockStart - name is a required parameter');
  if (!is_null($this->__blockName))
   trigger_error('LayoutHelper::blockStart - Blocks cannot overlap');
  
  $this->__blockName = $name;
  ob_start();
  return null;
 }
 
 function blockEnd(&$view){
  $buffer = @ob_get_contents();
  @ob_end_clean();
  $out = $buffer
  $view->viewVars[$this->__blockName . '_for_layout'] = $out;
  $this->__blockName = null
 }

 function output($var) {
  if (isset($var) && $var != null)
  echo $var;
 }
}

2. Setting up the content

For setting up the content that we want to send to the layout, we use the Helper

$layout = $this->Helpers->load('Layout');
$layout->blockStart('custom_content');

Right after this, we specify the content that will be sent to the layout

<div>Custom content</div>

and we close the block

$layout->blockEnd($this);

3. Show the content

For showing the content on the layout, we add the following

$layout = $this->Helpers->load('Layout');
$layout->output($custom_content_for_layout);

As we can see, this is really simple and also very useful when trying to customize the content on the layout according to the view we are loading

I wrote this article also on The Bakery