Ajax contour forms and zurb abide

This post is about implementing the contour forms package for Umbraco and still trying to follow best practices with regard to loading javascripts just before the closing body tag. I also implemented an ajax form submission method which grabs the resulting success message. It also uses zurb’s abide javascript validation library.

Here it is in action…

The general run-down of what was needed is as follows:

  1. update contours ‘form.cshtml’ to include the necessary classes and html5 attributes that make abide work
  2. remove references to unobtrusive javascript, jquery.ui (which is a big library) and the contour scripts.
  3. add ‘data-val-‘ attributes to form fields which allow abide to check required fields and display appropriate messages and validate fields against regular expressions. All of these values are coming from the form set up in the Umbraco back-office
  4. set up the abide component
  5. set up the ajax submit

Updating the form.cshtml template

The main bits done here were…

  • Put the ‘MessageOnSubmit’ property inside its own div so that the ajax submit could grab that div and output it
  • Remove references to Html.EnableClientValidation, Html.EnableUnobtrusiveJavaScript and the partial FormViewResolver.GetScriptView. The reason for this is that these references rely on jquery already being loaded. In my implementation, jquery isn’t loaded until just before the closing body tag, so this throws an error. The disadvantage here obviously is that the contour script manager isn’t loaded, meaning that only simple single page, non-conditional forms can be handled. However, for the purposes of the site, this isn’t a problem.
  • Add foundation classes to style the forms. This included adding the relevant markup to output the form using foundations grid system

Here’s the code inside the loop that outputs each field…

@foreach (FieldViewModel f in fs.Fields)
{
 bool hidden = f.HasCondition && f.ConditionActionType == FieldConditionActionType.Show;
 <div class="row">
     <div class="@f.CssClass" @{if (hidden){<text> style="display: none"</text>}}>
     @if(!f.HideLabel){
       <div class="columns small-12 medium-6">
       <label for="@f.Id" class="fieldLabel">
         @Html.Raw(f.Caption) 
         @if (f.ShowIndicator){
           <span class="contourIndicator">@Model.Indicator</span>
         }
        </label>
      </div>
      }
   <div class="columns small-12 @(!f.HideLabel ? "medium-6" : string.Empty)">
     @Html.Partial(FieldViewResolver.GetFieldView(Model.FormId, f.FieldTypeName, f.Caption), f)
     @if(f.Mandatory){
        <small class="error">@f.RequiredErrorMessage</small>
     }
     @if (Model.ShowFieldValidaton){ 
        @Html.ValidationMessage(f.Id, new { @class="error" })
     }
     @if (!string.IsNullOrEmpty(f.ToolTip)){
       <small>@f.ToolTip</small>
     }
   </div>
</div>
</div>
}

Data-val- attributes

For abide validation to work, on required fields the html5 ‘required’ attribute needs to be specified as well as a number of optional ‘data-val-‘ attributes. So, it was a case of going through the form field partials and adding the relevant attributes. Here’s the one for the text field…

@model Umbraco.Forms.Mvc.Models.FieldViewModel
<input type="text" name="@Model.Name" id="@Model.Id" class="text" value="@Model.Value" maxlength="500"
@{if(Model.Mandatory || Model.Validate){<text>data-val="true"</text>}}
@{if (Model.Mandatory) {<text> data-val-required="@Model.RequiredErrorMessage" required="required"</text>}}
@{if (Model.Validate) {<text> data-val-regex="@Model.InvalidErrorMessage" data-regex="@Html.Raw(Model.Regex)"</text>}}
/>

A few things to notice then – If the field is marked as mandatory the html5 ‘required’ attribute is added. To show the abide validator for required fields, the ‘data-val-required’ attribute is added using the required field message specified in the Umbraco back-office. The same is true if the text field needs validating against a regular expression. The invalid input message is specified using the ‘data-val-regex’ attribute and the actual regular expression to validate against is specified with the ‘data-regex’ attribute.

Setting up abide

This is the easiest bit. As long as jquery is loaded and the main foundation script, the ‘foundation.abide.js’ component just needs loading after it. This is my grunt file extract…

files: {
 'js/lcc.js': [
    'js/jquery-1.11.0.js',
    'bower_components/foundation/js/foundation/foundation.js',
    'bower_components/foundation/js/foundation/foundation.abide.js',
    //...

Ajax submission

The final bit is to set up the ajax submit. For this I created a little javascript module, which you can grab here. The main 2 bits of the code are the event handler to perform the ajax submit and the actual submission itself.

self.settings.contourForms.on('valid.fndtn.abide', function () {
  self.ajaxRequest.apply($(this), [self]);
  return false;
});

The ‘contourForms’ element is just defined as $(‘.contour form’). The ‘valid.fndtn.abide’ is the event that is fired on a valid form submission. This calls the ajaxRequest function using the form as the ‘this’ object.

Here’s a snippet of the ajaxRequest method…

$.ajax({
 url: formUrl,
 type: formMethod,
 data: formData,
 beforeSend: function () {
   waitingMsg.insertBefore(form);
 },
 complete: function () {
   waitingMsg.hide();
 },
 success: function (data) {
   var formContainer = $('#' + containerId);
   formContainer.html($('#' + containerId, data));
   $('form', formContainer).submit(function () {
     scope.ajaxRequest.apply($(this), [scope]);
     return false;
   });
 }
});

The variables in the above code are just grabbed from the form itself, such as the formUrl, formMethod etc. The formData variable is the result of calling ‘form.serialize().’ 3 functions are defined – 1 to show a waiting message, 1 once the form has been submitted, and 1 on successful form submission. The data returned by the ajax submission is the html from the resulting page, so its important to just grab the same form from that page and replace the old html with the new. If it was successful, the success message will be displayed, if not, the validation errors will be shown. One key thing is to re-bind the submit event handler on the new form, otherwise if the user submits the form again, it won’t be an ajax submission.

Conclusion

There you have it, a fairly elegant solution for presenting simple contour forms. We get the benefit of being able to create the form in the back-office with contours configuration options and still following html best practices. In addition, if, for some reason javascript isn’t available, the form still works in the usual way.

Any comments or suggestions for improvement very welcome.