<rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/" xmlns:copyright="http://blogs.law.harvard.edu/tech/rss" xmlns:image="http://purl.org/rss/1.0/modules/image/">
    <channel>
        <title>Web Development</title>
        <link>http://andrewhanson.net/blog/category/1.aspx</link>
        <description>Posts related mostly to developing applications on the web.</description>
        <language>en-US</language>
        <copyright>Andrew Hanson</copyright>
        <generator>Subtext Version 2.1.2.2</generator>
        <item>
            <title>Saving Object Graphs in One Action using Asp.Net MVC</title>
            <link>http://codinginertia.com/blog/archive/2009/08/10/saving-object-graphs-in-one-action-using-asp.net-mvc.aspx</link>
            <description>&lt;p&gt;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.&lt;/p&gt;
&lt;p&gt;   &lt;br /&gt;
&lt;strong&gt;The Scenario&lt;/strong&gt;    &lt;br /&gt;
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:&lt;/p&gt;
&lt;p&gt;   &lt;br /&gt;
 &lt;a href="http://andrewhanson.net/blog/images/andrewhanson_net/blog/WindowsLiveWriter/SavingObjectGraphsinOneActionusin.NetMVC_FC1/Mantle_NewDefinition_2.png"&gt;&lt;img height="582" width="557" border="0" style="border: 0px none ;" alt="Mantle_NewDefinition" src="http://andrewhanson.net/blog/images/andrewhanson_net/blog/WindowsLiveWriter/SavingObjectGraphsinOneActionusin.NetMVC_FC1/Mantle_NewDefinition_thumb.png" /&gt;&lt;/a&gt; &lt;/p&gt;
&lt;p&gt;   &lt;br /&gt;
&lt;br /&gt;
&lt;strong&gt;The Solution&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;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?.&lt;/p&gt;
&lt;p&gt; &lt;/p&gt;
&lt;p&gt;In my action method I added a parameter that is an array of PropertyDefinition objects resulting in the following method signature.&lt;/p&gt;
&lt;div&gt;
&lt;div style="border-style: none; padding: 0px; overflow: visible; font-size: 8pt; width: 100%; color: black; line-height: 12pt; font-family: consolas,'Courier New',courier,monospace; background-color: rgb(244, 244, 244);"&gt;
&lt;pre style="border-style: none; margin: 0em; padding: 0px; overflow: visible; font-size: 8pt; width: 100%; color: black; line-height: 12pt; font-family: consolas,'Courier New',courier,monospace; background-color: white;"&gt;[AcceptVerbs(HttpVerbs.Post)]&lt;/pre&gt;
&lt;pre style="border-style: none; margin: 0em; padding: 0px; overflow: visible; font-size: 8pt; width: 100%; color: black; line-height: 12pt; font-family: consolas,'Courier New',courier,monospace; background-color: rgb(244, 244, 244);"&gt;&lt;p&gt;&lt;span style="color: rgb(0, 0, 255);"&gt;public&lt;/span&gt; ActionResult Create(ContentItemDefinition ItemDefinition, &lt;span style="color: rgb(0, 0, 255);" /&gt;&lt;/p&gt;&lt;p&gt;&lt;span style="color: rgb(0, 0, 255);"&gt;string&lt;/span&gt; ContentTypeId, PropertyDefinition[] propertyDefinitions)&lt;/p&gt;&lt;p&gt;{....}&lt;/p&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt; &lt;/p&gt;
&lt;p&gt;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.  &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;The key to remember here is that the while you can send as many items as you want you &lt;strong&gt;MUST&lt;/strong&gt; maintain a &lt;strong&gt;sequential&lt;/strong&gt; 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.&lt;/p&gt;
&lt;div&gt;
&lt;pre style="border-style: none; margin: 0em; padding: 0px; overflow: visible; font-size: 8pt; width: 100%; color: black; line-height: 12pt; font-family: consolas,'Courier New',courier,monospace; background-color: rgb(244, 244, 244);"&gt;&lt;span style="color: rgb(0, 0, 255);"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: rgb(128, 0, 0);"&gt;input&lt;/span&gt; &lt;span style="color: rgb(255, 0, 0);"&gt;name&lt;/span&gt;&lt;span style="color: rgb(0, 0, 255);"&gt;="propertyDefinitions[0].Name"&lt;/span&gt; &lt;span style="color: rgb(255, 0, 0);"&gt;value&lt;/span&gt;&lt;span style="color: rgb(0, 0, 255);"&gt;="Property1"&lt;/span&gt; &lt;span style="color: rgb(255, 0, 0);"&gt;type&lt;/span&gt;&lt;span style="color: rgb(0, 0, 255);"&gt;="hidden"&lt;/span&gt;&lt;span style="color: rgb(0, 0, 255);"&gt;/&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 0, 255);"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: rgb(128, 0, 0);"&gt;input&lt;/span&gt; &lt;span style="color: rgb(255, 0, 0);"&gt;name&lt;/span&gt;&lt;span style="color: rgb(0, 0, 255);"&gt;="propertyDefinitions[1].Name"&lt;/span&gt; &lt;span style="color: rgb(255, 0, 0);"&gt;value&lt;/span&gt;&lt;span style="color: rgb(0, 0, 255);"&gt;="Property2"&lt;/span&gt; &lt;span style="color: rgb(255, 0, 0);"&gt;type&lt;/span&gt;&lt;span style="color: rgb(0, 0, 255);"&gt;="hidden"&lt;/span&gt;&lt;span style="color: rgb(0, 0, 255);"&gt;/&amp;gt;&lt;br /&gt;&lt;/span&gt; &lt;br /&gt;&lt;span style="color: rgb(0, 0, 255);"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: rgb(128, 0, 0);"&gt;input&lt;/span&gt; &lt;span style="color: rgb(255, 0, 0);"&gt;name&lt;/span&gt;&lt;span style="color: rgb(0, 0, 255);"&gt;="propertyDefinitions[5].Name"&lt;/span&gt; &lt;span style="color: rgb(255, 0, 0);"&gt;value&lt;/span&gt;&lt;span style="color: rgb(0, 0, 255);"&gt;="Property3"&lt;/span&gt; &lt;span style="color: rgb(255, 0, 0);"&gt;type&lt;/span&gt;&lt;span style="color: rgb(0, 0, 255);"&gt;="hidden"&lt;/span&gt;&lt;span style="color: rgb(0, 0, 255);"&gt;/&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 0, 255);"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: rgb(128, 0, 0);"&gt;input&lt;/span&gt; &lt;span style="color: rgb(255, 0, 0);"&gt;name&lt;/span&gt;&lt;span style="color: rgb(0, 0, 255);"&gt;="propertyDefinitions[6].Name"&lt;/span&gt; &lt;span style="color: rgb(255, 0, 0);"&gt;value&lt;/span&gt;&lt;span style="color: rgb(0, 0, 255);"&gt;="Property4"&lt;/span&gt; &lt;span style="color: rgb(255, 0, 0);"&gt;type&lt;/span&gt;&lt;span style="color: rgb(0, 0, 255);"&gt;="hidden"&lt;/span&gt;&lt;span style="color: rgb(0, 0, 255);"&gt;/&amp;gt;&lt;/span&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt; &lt;/p&gt;
&lt;p&gt;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.&lt;/p&gt;
&lt;p&gt;You can find all of the code in the &lt;a href="http://code.google.com/p/mantle-cms/source/browse/trunk/Mantle.AdminWeb/Views/ContentAdmin/New.aspx" target="_blank"&gt;Mantle-CMS SVN repository&lt;/a&gt; on Google code but I've added the interesting parts here for a quick read. &lt;/p&gt;
&lt;p&gt; &lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Adding a new Property Definition&lt;/strong&gt;&lt;/p&gt;
&lt;div&gt;
&lt;pre style="border-style: none; margin: 0em; padding: 0px; overflow: visible; font-size: 8pt; width: 100%; color: black; line-height: 12pt; font-family: consolas,'Courier New',courier,monospace; background-color: rgb(244, 244, 244);"&gt;&lt;span style="color: rgb(0, 0, 255);"&gt;var&lt;/span&gt; PropertyDefinitions = &lt;span style="color: rgb(0, 0, 255);"&gt;new&lt;/span&gt; Array();&lt;br /&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 128, 0);"&gt;/*...*/&lt;/span&gt;  &lt;span style="color: rgb(0, 0, 255);"&gt;function&lt;/span&gt; addProperty_Click(e) {&lt;br /&gt;    &lt;span style="color: rgb(0, 0, 255);"&gt;var&lt;/span&gt; name = $(&lt;span style="color: rgb(0, 96, 128);"&gt;"#contentDefinitionForm :input[name=PropertyName]"&lt;/span&gt;).val();&lt;br /&gt;    &lt;span style="color: rgb(0, 0, 255);"&gt;var&lt;/span&gt; dataType = $(&lt;span style="color: rgb(0, 96, 128);"&gt;"#contentDefinitionForm :input[name=PropertyDataType]"&lt;/span&gt;).val();&lt;br /&gt;    &lt;span style="color: rgb(0, 0, 255);"&gt;var&lt;/span&gt; description = $(&lt;span style="color: rgb(0, 96, 128);"&gt;"#contentDefinitionForm :input[name=PropertyDescription]"&lt;/span&gt;).val();&lt;br /&gt;&lt;br /&gt;    &lt;span style="color: rgb(0, 128, 0);"&gt;/*.... Validate inputs here ..*/&lt;/span&gt;  &lt;br /&gt;    &lt;span style="color: rgb(0, 128, 0);"&gt;&lt;br /&gt;    //Add a new item to PropertyDefintions array&lt;/span&gt;     &lt;span style="color: rgb(0, 0, 255);"&gt;&lt;br /&gt;    var&lt;/span&gt; rowId = PropertyDefinitions.length;&lt;br /&gt;    PropertyDefinitions[rowId] = &lt;span style="color: rgb(0, 0, 255);"&gt;new&lt;/span&gt; PropertyDefinition(name, dataType, description);&lt;br /&gt;&lt;br /&gt;    &lt;span style="color: rgb(0, 128, 0);"&gt;//Display the new property&lt;/span&gt;     &lt;span style="color: rgb(0, 0, 255);"&gt;var&lt;/span&gt; propertyList = $(&lt;span style="color: rgb(0, 96, 128);"&gt;"#propertyGrid"&lt;/span&gt;);&lt;br /&gt;    $(&lt;span style="color: rgb(0, 96, 128);"&gt;"#propertyGrid"&lt;/span&gt;)&lt;br /&gt;      .append(&lt;span style="color: rgb(0, 96, 128);"&gt;"&amp;lt;tr id='propertyRow"&lt;/span&gt; + rowId + &lt;span style="color: rgb(0, 96, 128);"&gt;"'&amp;gt;"&lt;/span&gt; +&lt;br /&gt;       &lt;span style="color: rgb(0, 96, 128);"&gt;&amp;lt;td&amp;gt;"&lt;/span&gt; + name + &lt;span style="color: rgb(0, 96, 128);"&gt;"&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;"&lt;/span&gt; + dataType + &lt;span style="color: rgb(0, 96, 128);"&gt;"&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;"&lt;/span&gt; + description + &lt;span style="color: rgb(0, 96, 128);"&gt;"&amp;lt;/td&amp;gt;"&lt;/span&gt; +&lt;br /&gt;         &lt;span style="color: rgb(0, 96, 128);"&gt;"&amp;lt;td&amp;gt;&amp;lt;a href='#' onclick='removeProperty_Click(\""&lt;/span&gt; + rowId + &lt;span style="color: rgb(0, 96, 128);"&gt;"\");'&amp;gt;Remove&amp;lt;/a&amp;gt;"&lt;/span&gt; + &lt;span style="color: rgb(0, 96, 128);"&gt;"&amp;lt;/td&amp;gt;"&lt;/span&gt; +&lt;br /&gt;       &lt;span style="color: rgb(0, 96, 128);"&gt;"&amp;lt;/tr&amp;gt;"&lt;/span&gt;);&lt;br /&gt;    &lt;br /&gt;    &lt;span style="color: rgb(0, 0, 255);"&gt;return&lt;/span&gt; &lt;span style="color: rgb(0, 0, 255);"&gt;false&lt;/span&gt;;&lt;br /&gt;}&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt; &lt;/p&gt;
&lt;p&gt;&lt;strong&gt;On Form Submit&lt;/strong&gt;&lt;/p&gt;
&lt;div&gt;
&lt;pre style="border-style: none; margin: 0em; padding: 0px; overflow: visible; font-size: 8pt; width: 100%; color: black; line-height: 12pt; font-family: consolas,'Courier New',courier,monospace; background-color: rgb(244, 244, 244);"&gt;$(document).ready(&lt;br /&gt; &lt;span style="color: rgb(0, 0, 255);"&gt;function&lt;/span&gt;() {&lt;br /&gt;                &lt;br /&gt;   &lt;span style="color: rgb(0, 128, 0);"&gt;//Capture form submit to added hidden fields for added/removed properties&lt;/span&gt;&lt;br /&gt;   $(&lt;span style="color: rgb(0, 96, 128);"&gt;'#contentDefinitionForm'&lt;/span&gt;).submit(&lt;span style="color: rgb(0, 0, 255);"&gt;function&lt;/span&gt;() {&lt;br /&gt;&lt;br /&gt;   &lt;span style="color: rgb(0, 128, 0);"&gt;//new properties, with sequential index&lt;/span&gt;    &lt;span style="color: rgb(0, 0, 255);"&gt;for&lt;/span&gt; (i = 0; i &amp;lt; PropertyDefinitions.length; i++) {&lt;br /&gt;      $(&lt;span style="color: rgb(0, 96, 128);"&gt;"#contentDefinitionForm"&lt;/span&gt;).append(&lt;span style="color: rgb(0, 96, 128);"&gt;"&amp;lt;input name='propertyDefinitions["&lt;/span&gt; + i + &lt;span style="color: rgb(0, 96, 128);"&gt;"].Name'" +&lt;br /&gt;           "value='"&lt;/span&gt; + PropertyDefinitions[i].Name + &lt;span style="color: rgb(0, 96, 128);"&gt;"' &lt;/span&gt;&lt;span style="color: rgb(0, 96, 128);"&gt;type='hidden'&lt;/span&gt;&lt;span style="color: rgb(0, 96, 128);"&gt;/&amp;gt;"&lt;/span&gt;);&lt;br /&gt;      $(&lt;span style="color: rgb(0, 96, 128);"&gt;"#contentDefinitionForm"&lt;/span&gt;).append(&lt;span style="color: rgb(0, 96, 128);"&gt;"&amp;lt;input name='propertyDefinitions["&lt;/span&gt; + i + &lt;span style="color: rgb(0, 96, 128);"&gt;"].DataType'" +&lt;br /&gt;          "value='"&lt;/span&gt; + PropertyDefinitions[i].DataType + &lt;span style="color: rgb(0, 96, 128);"&gt;"' &lt;/span&gt;&lt;span style="color: rgb(0, 96, 128);"&gt;type='hidden'&lt;/span&gt;&lt;span style="color: rgb(0, 96, 128);"&gt;/&amp;gt;"&lt;/span&gt;);&lt;br /&gt;      $(&lt;span style="color: rgb(0, 96, 128);"&gt;"#contentDefinitionForm"&lt;/span&gt;).append(&lt;span style="color: rgb(0, 96, 128);"&gt;"&amp;lt;input name='propertyDefinitions["&lt;/span&gt; + i + &lt;span style="color: rgb(0, 96, 128);"&gt;"].Description'" + &lt;br /&gt;          "value='"&lt;/span&gt; + PropertyDefinitions[i].Description + &lt;span style="color: rgb(0, 96, 128);"&gt;"' &lt;/span&gt;&lt;span style="color: rgb(0, 96, 128);"&gt;type='hidden'&lt;/span&gt;&lt;span style="color: rgb(0, 96, 128);"&gt;/&amp;gt;"&lt;/span&gt;);&lt;br /&gt;   }&lt;br /&gt;&lt;br /&gt;   });&lt;br /&gt;   }&lt;br /&gt;);&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt; &lt;/p&gt;
&lt;p&gt;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.&lt;/p&gt;
&lt;p&gt; &lt;/p&gt;
&lt;div class="wlWriterSmartContent" id="scid:0767317B-992E-4b12-91E0-4F059A8CECA8:f6ab50f7-ae06-427a-90cd-0be646c4526b" style="margin: 0px; padding: 0px; display: inline;"&gt;Technorati Tags: &lt;a href="http://technorati.com/tags/aspnet-mvc" rel="tag"&gt;aspnet-mvc&lt;/a&gt;,&lt;a href="http://technorati.com/tags/C#" rel="tag"&gt;C#&lt;/a&gt;,&lt;a href="http://technorati.com/tags/jquery" rel="tag"&gt;jquery&lt;/a&gt;&lt;/div&gt;&lt;img src="http://codinginertia.com/blog/aggbug/26.aspx" width="1" height="1" /&gt;</description>
            <dc:creator>Andrew Hanson</dc:creator>
            <guid>http://codinginertia.com/blog/archive/2009/08/10/saving-object-graphs-in-one-action-using-asp.net-mvc.aspx</guid>
            <pubDate>Mon, 10 Aug 2009 07:12:43 GMT</pubDate>
            <comments>http://codinginertia.com/blog/archive/2009/08/10/saving-object-graphs-in-one-action-using-asp.net-mvc.aspx#feedback</comments>
            <wfw:commentRss>http://codinginertia.com/blog/comments/commentRss/26.aspx</wfw:commentRss>
        </item>
    </channel>
</rss>