The other day I was talking some fellow developers about web architecture techniques. I brought up the use of jQuery templates instead of another proposed technique. A case was made the template plugin was not good for progressive enhancement. The objection was it forces you to maintain two separate HTML instances to render the same content. One instance for the initial server-side rendering and one for the client-side template. I knew this was not the case, so today I will show how to execute progressive enhancement while using ASP.NET MVC partial views.

There are two things you need to leverage to make this work. First, the model you are using should only use string properties. This is a bit unorthodox, but remember in the world of Model-View-Controller or MVVM, SOA, you tend to map objects between layers so they conform to their intended use.

The Contact entity now contains only string properties:

public class Contact {

    public string FirstName {get; set; }
    public string LastName {get; set; }
    public string PhoneNumber {get; set; }
    public string Street {get; set; }
    public string City {get; set; }
    public string State {get; set; }
    public string PostalCode {get; set; }

}


I know some of you are freaking out, but calm down. You can simply transform these values to the types you actually need in you application’s natural pipeline. As for validation, not doing in this little layer. I ultimately do validation on the client for UX purposes and for real in the business layer.

Now in your controller, I am just going to use HomeController today, you need to add a new View called ContactForm. It needs to have the
ChildActionOnly attribute applied.

[ChildActionOnly]
public ActionResult ContactForm() {


    return PartialView();

}


To make this work I need to add some code and parameters. The ContactForm partial view’s primary purpose is to render the desired contact, an empty new contact or the contact merge fields for the jQuery template plugin. I have added two parameters to the controller, Id and newContact. These are used to trigger the correct Contact object values.

[ChildActionOnly]
public ActionResult ContactForm(string Id, string newContact) {
   
    if (!string.IsNullOrEmpty(Id)) {
        ViewBag.Contact = contactServiceManager.GetContact(Id); ;
    } else {

        if (string.IsNullOrEmpty(newContact)) {

            ViewBag.Contact = new Contact()
            {
                ContactId = "${ContactId}",
                FirstName = "${FirstName}",
                LastName = "${LastName}",
                Business = "${Business}",
                Address1 = "${Address1}",
                Address2 = "${Address2}",
                City = "${City}",
                State = "${State}",
                PostalCode = "${PostalCode}",
                PhoneNumber = "${PhoneNumber}",
                EMail = "${EMail}",
                Comment = "${Comment}",
            };
       
        } else {

            ViewBag.Contact = new Contact();

        }

    }
 
    return PartialView("ContactForm");
}


The controller now checks the input parameters and creates a Contact object with the desired values. If there is an Id value it tries to retrieve the contact from the business layer. If not it checks to see if an empty Contact or a merge field Contact object is needed. 

The corresponding View is pretty straight forward. It simply fills the value of each INPUT tag with the appropriate values.

<div id="ContactEditForm">
  @using (Html.BeginForm()) {
    @Html.ValidationSummary(true)
   
<input type="hidden" name="Id" id="Id" value="@ViewBag.Contact.ContactId" />
   
<fieldset class="span-1">
     
<legend>@ViewBag.Contact.FirstName @ViewBag.Contact.LastName</legend>
     
<ul>
       
<li>
         
<label for="FirstName">
            First Name
</label><input id="FirstName" name="FirstName"
             
class="" value="@ViewBag.Contact.FirstName" /></li>
       
<li>
         
<label for="LastName">
            Last Name
</label><input id="LastName" name="LastName"
             
class="" value="@ViewBag.Contact.LastName" /></li>
       
<li>
         
<label for="Business">
            Business
</label><input id="Business" name="Business"
             
class="" value="@ViewBag.Contact.Business" /></li>
       
<li>
         
<label for="Address1">
            Address1
</label><input id="Address1" name="Address1"
             
class="" value="@ViewBag.Contact.Address1" /></li>
       
<li>
         
<label for="Address2">
            Address2
</label><input id="Address2" name="Address2"
              
class="" value="@ViewBag.Contact.Address2" /></li>
       
<li>
         
<label for="City">
            City
</label><input id="City" name="City"
             
class="" value="@ViewBag.Contact.City" /></li>
       
<li>
         
<label for="State">
            State
</label><input id="State" name="State"  
           
class="" value="@ViewBag.Contact.State" /></li>
       
<li>
         
<label for="PostalCode">
            Postal Code
</label><input id="PostalCode"
           
name="PostalCode" class="" value="@ViewBag.Contact.PostalCode" /></li>
       
<li>
         
<label for="PhoneNumber">
            Phone Number
</label><input id="PhoneNumber"
           
name="PhoneNumber" class="" value="@ViewBag.Contact.PhoneNumber" /></li>
       
<li>
         
<label for="EMail">
            E-Mail
</label><input id="EMail" name="EMail"
           
class="" value="@ViewBag.Contact.EMail" /></li>
       
<li>
         
<label for="Comment">
            Comment
</label><input id="Comment"
           
name="Comment" class="" value="@ViewBag.Contact.Comment" /></li>
       
<li>
         
<ul class="btn-List">
           
<li>
             
<button id="CancelButton" class="btn"
             
text="Cancel" type='button' tabindex="199">
                Cancel
</button>
           
</li>
           
<li>
             
<button id="SaveButton" class="btn default"
             
type='submit' tabindex="198">
                Save
</button>
           
</li>
         
</ul>
       
</li>
     
</ul>
   
</fieldset>
  }
</div>


So the way I like to do a Master List/Detail UX is to list the records in a table and display a dialog for editing purposes. But let’s say you have a user fixated on the great JavaScript scare of 1995 and has it disabled. In this scenario the edit link will not display the edit dialog, but instead take them to the edit page. 

On my Master page, the Index view in this example, the following code adds the ContactForm partial view content. It creates the jQuery Template plugin template needed to render the edit dialog.

<script id="EditFormTemplate" type="text/html">
 @{Html.RenderAction("ContactForm", new { Id = "", newContact = "" });}
 </script>


Now in the actual Edit view the following code will simply render the form in the desired format if JavaScript is turned off or someone actually does a direct link to the form.

@{Html.Action("ContactForm", new { Id = "", newContact = "" });}

Whew!!! That’s a lot to digest and we are not done! 

As far as opening the edit dialog and using the jQuery template plugin to generate the input form, the following jQuery code intercepts a click on all edit links, merges the Contact object and appends it to the dialog. If you are not familiar with the
jQueryUI dialog widget there are various examples on the web. The documentation is pretty complete as well. As for the jQuery Templates plugin you can start with the source code page and go from there. I know I really need to blog more about them and I will, I will….

$(".edit-link").click(function (e) {

  e.preventDefault();

  var jqxhr = $.ajax({
    url: GetRootUrl() + "Home/Contact?Id=" + $(this).attr("Id")
  })
            .success(function (result) {

              contact = result;

              $("#EditFormTemplate")
                        .tmpl(result)
                                .appendTo("#contactDlg");

              $("#contactDlg").dialog('open');

            })
            .error(function (qXHR, textStatus, errorThrown) {
              alert("ack! an error!\r\n" + textStatus + " " + errorThrown);
            });

});


All right, that just about does it for the core code that drives this solution. Seeing is believing. (Disclaimer, I use a pretty bad random content generator to give me some dummy data, so excuse that, it’s just placeholder to me).





New Dialog (using the same code as the Edit Dialog BTW)



Edit page, using the same ContactForm partial view.

If you are using ASP.NET WebForms you can easily translate this technique using Custom Controls (.ascx) instead of Partial Views.


So this is my quick and dirty first hack at solving a very real problem. I am sure I will improve and refine this over the coming months. Since I am just now getting into MVC I am also sure I made some faux pas with that too, but again it works and can be improved upon.