zaterdag 18 juni 2011

Denote required fields in asp.net MVC 3

I'm a big fan of the asp.net MVC framework.

The "convention over configuration" idea makes your projects more easily to understand by other developers looking into your code.
Maybe it's just me, but I think that MVC helps a lot in matching the pieces in your architectural design; it makes you write better code.

The true power of the MVC framework lies in its extensibility.
You can get very far using the "out of the box" features, but the asp.net team made it very easy to extend that standard functionality.

This post is an example of this extensibility.

I'm going to show you how you can automatically denote required fields on a form using an asterix (*).

So let's fire up Visual Studio and create a new MVC 3 project.


Step 1: The model

Right-click you Model folder, choose Add -> Class and name it QuestionModel.cs.

Add some properties: Id, LastName, FirstName, Company, JobTitle, Email, Question.
Tip: for simple properties you can type "prop" and hit tab twice.

Mark the required properties as "Required" using DataAnnotations (System.ComponentModel.DataAnnotations).

   1:      public class QuestionModel
   2:      {
   3:          [Key]
   4:          public int Id { get; set; }
   5:   
   6:          [Required]
   7:          public string LastName { get; set; }
   8:   
   9:          [Required]
  10:          public string FirstName { get; set; }
  11:   
  12:          public string Company { get; set; }
  13:   
  14:          public string JobTitle { get; set; }
  15:   
  16:          [Required]
  17:          public string Email { get; set; }
  18:   
  19:          [Required]
  20:          public string Question { get; set; }
  21:      }

Build your project (ctrl + shift + b), so MVC picks up your model.



Step 2: The controller

Starting from the "HomeController", create a new ActionResult methode called "Question" which returns a View with an instance of our QuestionModel as a parameter.

   1:  public class HomeController : Controller
   2:      {
   3:          // ...
   4:   
   5:          public ActionResult Question()
   6:          {
   7:              var model = new QuestionModel();
   8:   
   9:              return View(model);
  10:          }
  11:      }

Still in your controller class, right click the Question ActionResult method and choose "Add View..." to create your view.

Make it a "stongly-typed" view with it's "Model class" set to the "QuestionModel" and "Razor" as it's "view engine".


Step 3: The Extension

Our extension will check if a field is required and if so, place an asterix (*) after the textbox.

Create a new folder in your project and call it "Extensions".
Add a class to this folder calling HtmlExtensions.
   1:  public static class HtmlExtensions
   2:      {
   3:          public static MvcHtmlString RequiredFieldFor<TModel, TValue>(this HtmlHelper<TModel> html, Expression<Func<TModel, TValue>> expression)
   4:          {
   5:              // Get the metadata for the model
   6:              var metadata = ModelMetadata.FromLambdaExpression(expression, html.ViewData);
   7:   
   8:              // Check if the field is required
   9:              var isRequired = metadata
  10:                                  .ContainerType.GetProperty(metadata.PropertyName)
  11:                                  .GetCustomAttributes(typeof(RequiredAttribute), false)
  12:                                  .Length == 1;
  13:   
  14:              // If the field is required generate label with an asterix
  15:              if (isRequired)
  16:              {
  17:                  var labelText = "*";
  18:   
  19:                  var tag = new TagBuilder("label");
  20:                  tag.MergeAttributes(new Dictionary<string, object> { { "style", "color: red; margin-left: -5px; margin-right: 5px; vertical-align: top;" } });
  21:                  tag.SetInnerText(labelText);
  22:                  
  23:                  return MvcHtmlString.Create(tag.ToString(TagRenderMode.Normal));
  24:              }
  25:   
  26:              return null;
  27:          }
  28:      }

Again build your project so mvc can pick up the changes.


Step 4: The View

Open the view created at the end of step 2.

Reference your extension class:

   1:  @using RedAsterixExample.Extensions;

Call the extension method for every field (required an not required field):

   1:  @Html.RequiredFieldFor(model => model.LastName)

The complete view could look something like:

   1:  @model RedAsterixExample.Models.QuestionModel
   2:   
   3:  @{
   4:      ViewBag.Title = "Question";
   5:  }
   6:   
   7:  @using RedAsterixExample.Extensions;
   8:   
   9:  <h2>Question</h2>
  10:   
  11:  <script src="@Url.Content("~/Scripts/jquery.validate.min.js")" type="text/javascript"></script>
  12:  <script src="@Url.Content("~/Scripts/jquery.validate.unobtrusive.min.js")" type="text/javascript"></script>
  13:   
  14:  @using (Html.BeginForm())
  15:  {
  16:      @Html.ValidationSummary(true)
  17:      <fieldset>
  18:          @Html.HiddenFor(m => m.Id)
  19:   
  20:          <div class="editor-label">
  21:              @Html.LabelFor(model => model.LastName)
  22:          </div>
  23:          <div class="editor-field">
  24:              @Html.EditorFor(model => model.LastName)
  25:              @Html.RequiredFieldFor(model => model.LastName)
  26:              @Html.ValidationMessageFor(model => model.LastName)
  27:          </div>
  28:   
  29:          <div class="editor-label">
  30:              @Html.LabelFor(model => model.FirstName)
  31:          </div>
  32:          <div class="editor-field">
  33:              @Html.EditorFor(model => model.FirstName)
  34:              @Html.RequiredFieldFor(model => model.FirstName)
  35:              @Html.ValidationMessageFor(model => model.FirstName)
  36:          </div>
  37:   
  38:          <div class="editor-label">
  39:              @Html.LabelFor(model => model.Company)
  40:          </div>
  41:          <div class="editor-field">
  42:              @Html.EditorFor(model => model.Company)
  43:              @Html.RequiredFieldFor(model => model.Company)
  44:              @Html.ValidationMessageFor(model => model.Company)
  45:          </div>
  46:   
  47:          <div class="editor-label">
  48:              @Html.LabelFor(model => model.JobTitle)
  49:          </div>
  50:          <div class="editor-field">
  51:              @Html.EditorFor(model => model.JobTitle)
  52:              @Html.RequiredFieldFor(model => model.JobTitle)
  53:              @Html.ValidationMessageFor(model => model.JobTitle)
  54:          </div>
  55:   
  56:          <div class="editor-label">
  57:              @Html.LabelFor(model => model.Email)
  58:          </div>
  59:          <div class="editor-field">
  60:              @Html.EditorFor(model => model.Email)
  61:              @Html.RequiredFieldFor(model => model.Email)
  62:              @Html.ValidationMessageFor(model => model.Email)
  63:          </div>
  64:   
  65:          <div class="editor-label">
  66:              @Html.LabelFor(model => model.Question)
  67:          </div>
  68:          <div class="editor-field">
  69:              @Html.TextAreaFor(model => model.Question)
  70:              @Html.RequiredFieldFor(model => model.Question)
  71:              @Html.ValidationMessageFor(model => model.Question)
  72:          </div>
  73:   
  74:          <p>
  75:              <span>Fields denoted with a (*) are required </span>
  76:              <br />
  77:              <input type="submit" value="Create" />
  78:          </p>
  79:      </fieldset>
  80:  }
  81:   
  82:  <div>
  83:      @Html.ActionLink("Back to List", "Index")
  84:  </div>

When you run the application, you will see that the red asterix (*) is only added for the required fields.

1 opmerking:

  1. I've made a modified version based on your code, please check this here:
    http://stackoverflow.com/questions/2974825/define-markup-for-required-fields-in-view-in-asp-net-mvc-2-0/9194220#9194220

    BeantwoordenVerwijderen