Building Rich Forms with JQuery and Plugins

One of the things I have been focusing on is building live forms for gathering large amounts of data in a rich fashion. For the personal project that I am working on there is a large amount of data that must be collected and putting it all on one page would make for a difficult user interface. So my goal became to turn this into a wizard, which makes the most sense.  To do this, I decided to use the formwizard plugin.

Understand that this plugin takes a form whose various “pages” are separated by

with a class of step; it is also recommended that you apply an id attribute to these div sections.  The setup for the plugin is below:

   1: $("#innerContent form").formwizard({

   2:     formPluginEnabled: true,

   3:     historyEnabled: true,

   4:     validationEnabled: true,

   5:     focusFirstInput: true,

   6:     showBackOnFirstStep: true,

   7:     next: "#mynextButton",

   8:     back: "#mybackButton",

   9:     onShow: function (step) {

  10:         stepChange(step);

  11:     }

  12: },

  13: { /* Validation Configuration - None needed, MS takes care of it */ },

  14: {

  15:     success: function (data) {

  16:         $("#innerContent form").formwizard("show", "#" + data.CurrentStep);

  17:         $("button").removeClass("ui-state-hover");

  18:     }

  19: });

Some things I would like to point out.  First is the onShow event.  This is a custom event that I added because the existing event that was similar was just not quite what I wanted.  I will provide my updated version of the plugin at the bottom.  I have communicated the change to the person who wrote the plugin and he is considering it for the next version.

This application uses ASP .NET MVC 2 and leverages the client-side validation generation done by .NET DataAnnotations formally of the DynamicData library.  Here is the Speaker class, complete with one validation attribute:

   1: public class Speaker : ModelBase

   2: {

   3:     [DisplayName("First Name")]

   4:     [Required(ErrorMessage = "First Name is required")]

   5:     public string FirstName { get; set; }

   6:  

   7:     [DisplayName("Last Name")]

   8:     public string LastName { get; set; }

   9:  

  10:     [DisplayName("Twitter")]

  11:     public string TwitterHandle { get; set; }

  12:  

  13:     [DisplayName("Blog URL")]

  14:     public string BlogUrl { get; set; }

  15:  

  16:     [DisplayName("Bio")]

  17:     public string Bio { get; set; }

  18:  

  19:     public string DisplayName

  20:     {

  21:         get { return string.Format("{0}, {1}", LastName, FirstName); }

  22:     }

  23:  

  24:     public override string[] ToArray()

  25:     {

  26:         return new string[]

  27:         {

  28:             DisplayName, TwitterHandle, BlogUrl

  29:         };

  30:     }

  31: }

The formwizard plugin does support validating the steps before moving to the next.  You must have the JQuery Validate library for this to work and set validationEnabled to true in the wizard setup.  The goal here is to allow MVC2 to generate the validation and then seamlessly be able to use it.

To achieve this, I needed to find the MicrosoftMVCJQueryValidation.js file because, for whatever reason, it does not ship as part of the MVC 2 templates found in Visual Studio 2010, I am told it ships as part of another package.  It is available at the bottom for download.  Once you have the file, include the following line on your page to apply the validation:

   1: 

This looks for forms on the page and then matches their inputs with metadata supplied from the DataAnnotations to generate the validation setup script.  However, I ran into a bit of a gotcha with this, but I will move that to later.

Now if you you were just going to move from page to page with no real effects and just show the inputs, you would be set at this point.  However, whats the fun in that, I wanted to use a modal to add Speakers to a grid. However, I also wanted this modal to be validated independently of the formwizard.  It was clear that I would have to manually invoke the validation because I didnt want the validation rules in the modal to prevent moving to the next screen.

First, this requires the inputs within the modal to be in a separate form, this is the markup:

   1:  <% Html.BeginForm(string.Empty, string.Empty, FormMethod.Post, new { @id = "speakerForm" }); %>
   2:  <div class="modal" id="speakerModal">
   3:      <% Html.RenderPartial("SpeakerModify", new Speaker()); %>
   4:      <p class="footer">
   5:          <%= Html.ActionLink("Add Speaker", "StoreSpeaker",
   6:                   new { }, new { @class="fg-button ui-state-default ui-corner-all", @id="speakerSubmit" }) %>
   7:      </p>
   8:  </div>
   9:  <% Html.EndForm(); %>

.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, “Courier New”, courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }

Now, I learned something very interesting here, though it makes sense after the fact. You MUST use Html.BeginForm to create a form which MVC will attempt to tie JQuery validation to.  I spent hours trying to understand why my validation did not work and it was because I initially went with a simple for this.  I had to put debgger statements in the MicrosoftMVCJQuedryValidation.js file to totally understand this.

For formwizard you will need this to be outside the main form, which having its own form requires anyway, however, some may know that the JQuery Dialog component is actually moved to the end of all markup during its setup.  Thus you need to add some additional code to the setup, like such:

   1: $("#speakerModal").dialog({

   2:     autoOpen: false,

   3:     modal: true,

   4:     title: "Modify Speaker",

   5:     width: 500,

   6:     height: 'auto',

   7:     resizable: false,

   8:     draggable: false,

   9:     open: function () {

  10:         $("#FirstName").focus();

  11:     },

  12:     close: function () {

  13:         $(this).find(":input").val("");

  14:     }

  15: }).parent().appendTo($("#speakerForm"));

Basically, the dialog

starts in the form, and thus we will move it back into the after it has been created, as the process of creation moves it out of the form, which causes problems for the validation linking when the MVC onReady event fires, this is the reason why the form above had an id is specified.

Finally, we want to submit the form data to the backend Controller action and use the standard model binder to create the object that can be applied to our model for saving later. Here is the code that checks the data is valid and initiates a POST action to the controller method:

   1: $("#speakerSubmit").click(function () {

   2:         // ensure the form is valid

   3:         if ($("#speakerForm").valid()) {

   4:             var href = $(this).attr('href');

   5:             var formData = $("#speakerModal :input").serializeObject();

   6:  

   7:             $.post(href, formData, function (success) {

   8:                 $("#speakerModal").dialog("close");

   9:                 //UpdateGrid(success.Data);

  10:             });

  11:         }

  12:  

  13:         return false;

  14:     });

This code utilizes the serializeObject plugin which, given an array of JQuery form inputs, creates a JSON object which can be passed to the various Ajax related JQuery functions.  Also, notice how we are using the href attribute from the link to specify where this data is to be posted, this way we can easily change the URL in the View via the HTML Helper ActionLink function and affect this.  It just makes more sense then hardcoding the URL in the JavaScript itself.

A word of caution about JQuery validate and JQuery selection.  In order for the validate to work you MUST select the tag.  JQuery does some special logic with this selection.  The form will then display its various labels and inputs in an array.  It basically looks different from a traditional selection of a

or other elements, and it this difference which allows JQuery validate to work property; this is the reason for the id attribute being specified for the Speaker form.

Finally we have the relatively simple controller action:

   1: [HttpPost]

   2: public ActionResult StoreSpeaker(Speaker speaker)

   3: {

   4:     CurrentConference.Speakers.Add(speaker);

   5:     return Json(new SubmitActionReturnViewModel

   6:     {

   7:         IsValid = true,

   8:         Data = speaker.ToArray()

   9:     });

  10: }

This simply returns a JSON object to be used by the view.  Our next step is to take the return data, close the dialog and update the table to show the new speaker.  This is using a collection methodology and waiting to save everything till the end.

Using MVC 2 it is easily possible to use the DataAnnotations model to provide a means to easy way to generate this validation from a central place and allow it to be usable for both the client and the server.  This is perhaps my favorite feature in ASP .NET MVC.  I think its incredibly useful and a great to help developers continue to enforce DRY (Dont Repeat Yourself), in an area that before we had to do everything on the sever to achieve.

http://cid-630ed6f198ebc3a4.skydrive.live.com/embedicon.aspx/Public/Scripts.zip

Advertisements

5 thoughts on “Building Rich Forms with JQuery and Plugins

  1. Sorry for my bad english. Thank you so much for your good post. Your post helped me in my college assignment, If you can provide me more details please email me.

    Like

  2. Do you mean to be telling us, “Anonymous,” that your college assignment involved the creation of nonsensical spam input? 'sheesh!

    Like

  3. I agree with most of your points, but some need to be discussed further, I will hold a small talk with my buddies and perhaps I will ask you some suggestion shortly.

    – Henry

    Like

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s