Using an MVC PartialView With jQuery Templates Plugin

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.

This example is going to use the Contacts application I used last year to demonstrate jQuery AJAX using WCF, at least the core application concept. The application uses the ASP.NET MVC 3 Razor ViewEngine.

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).
 

Edit Dialog
 

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.

Download the jQuery Templating Sample

Share This Article With Your Friends!