I learned a good Asp.Net MVC tip the other day that I must have missed when it was the released, the ability to bind collections of objects directly to a parameter of an Action Method.
The Scenario
When working with the default Mantle-CMS web admin tool I ran into a situation in which I wanted to create a new Content Definition and all it's Properties Definitions and eventually Validation Rules in one shot. The user interface that I came up with was this:
The Solution
I knew that using JQuery it would be fairly straight forward to dynamically create the HTML and any necessary input properties for each new property definition but what was the right way to pass all these values back to my Action Method?.
In my action method I added a parameter that is an array of PropertyDefinition objects resulting in the following method signature.
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Create(ContentItemDefinition ItemDefinition,
string ContentTypeId, PropertyDefinition[] propertyDefinitions)
{....}
This is really straight forward and makes my method really easy to test but how do I wire it up? For that you need to create input elements named "propertyDefinitions[0].Name", "propertyDefinitions[1].Name" and so on for all the items you wish to create.
The key to remember here is that the while you can send as many items as you want you MUST maintain a sequential index number starting at 0. For example if you had the following HTML you would end up adding only 2 properties instead of the 4 you were expecting.
<input name="propertyDefinitions[0].Name" value="Property1" type="hidden"/>
<input name="propertyDefinitions[1].Name" value="Property2" type="hidden"/>
<input name="propertyDefinitions[5].Name" value="Property3" type="hidden"/>
<input name="propertyDefinitions[6].Name" value="Property4" type="hidden"/>
This was an important point in my application because I wanted the user to be able to add an remove multiple properties in the UI before submitting the values, so I was required to renumber the collection indexes before submitting the form to my action method. Because of this instead of writing out the HTML for the properties as they were created I chose to keep the new properties in a JavaScript array and only create the required HTML elements right before the form was submitted.
You can find all of the code in the Mantle-CMS SVN repository on Google code but I've added the interesting parts here for a quick read.
Adding a new Property Definition
var PropertyDefinitions = new Array();
/*...*/ function addProperty_Click(e) {
var name = $("#contentDefinitionForm :input[name=PropertyName]").val();
var dataType = $("#contentDefinitionForm :input[name=PropertyDataType]").val();
var description = $("#contentDefinitionForm :input[name=PropertyDescription]").val();
/*.... Validate inputs here ..*/
//Add a new item to PropertyDefintions array
var rowId = PropertyDefinitions.length;
PropertyDefinitions[rowId] = new PropertyDefinition(name, dataType, description);
//Display the new property var propertyList = $("#propertyGrid");
$("#propertyGrid")
.append("<tr id='propertyRow" + rowId + "'>" +
<td>" + name + "</td><td>" + dataType + "</td><td>" + description + "</td>" +
"<td><a href='#' onclick='removeProperty_Click(\"" + rowId + "\");'>Remove</a>" + "</td>" +
"</tr>");
return false;
}
On Form Submit
$(document).ready(
function() {
//Capture form submit to added hidden fields for added/removed properties
$('#contentDefinitionForm').submit(function() {
//new properties, with sequential index for (i = 0; i < PropertyDefinitions.length; i++) {
$("#contentDefinitionForm").append("<input name='propertyDefinitions[" + i + "].Name'" +
"value='" + PropertyDefinitions[i].Name + "' type='hidden'/>");
$("#contentDefinitionForm").append("<input name='propertyDefinitions[" + i + "].DataType'" +
"value='" + PropertyDefinitions[i].DataType + "' type='hidden'/>");
$("#contentDefinitionForm").append("<input name='propertyDefinitions[" + i + "].Description'" +
"value='" + PropertyDefinitions[i].Description + "' type='hidden'/>");
}
});
}
);
I was also able to reuse this same technique in my edit actions to capture new and remove properties and post them in one action by creating a RemovedDefinitions collection and Action Method parameter.
Technorati Tags:
aspnet-mvc,
C#,
jquery