I followed this approach related in the blogs above and added some details that may be useful for some - especially since I wanted to dynamically add any number of lines, but did not want to use AJAX for this (I only need to submit the form in the mail) . I also did not want to worry about storing consecutive identifiers. I wrote down a list of start and end dates:
Show model:
public class WhenViewModel : BaseViewModel { public List<DateViewModel> Dates { get; set; }
Start / End Date View Model:
public class DateViewModel { public string DateID { get; set; } public DateTime? StartDate { get; set; } public DateTime? EndDate { get; set; } }
Then using them on the page (with datepicker):
<div class="grid-8-12 clear" id="DatesBlock"> @{ foreach (DateViewModel d in Model.Dates) { @:<div class="grid-5-12 left clear"> @Html.Hidden("Dates.Index", d.DateID) @Html.Hidden("Dates[" + d.DateID + "].DateID", d.DateID) //ID again to populate the view model @Html.TextBox("Dates[" + d.DateID + "].StartDate", d.StartDate.Value.ToString("yyyy-MM-dd")) @:</div> @:<div class="grid-5-12"> @Html.TextBox("Dates[" + d.DateID + "].EndDate", d.EndDate.Value.ToString("yyyy-MM-dd")) @:</div> <script type="text/javascript"> $('input[name="Dates[@d.DateID].StartDate"]') .datepicker({ dateFormat: 'yy-mm-dd'}); $('input[name="Dates[@d.DateID].EndDate"]') .datepicker({dateFormat: 'yy-mm-dd'}); </script> } } </div> <a href="#" onclick="AddDatesRow()">Add Dates</a>
As described in the blog post described earlier in the @ErikTheVikings article, the collection is created by a repeating hidden element: @Html.Hidden("Dates.Index", d.DateID) for each entry in the collection on the page.
I wanted to add rows arbitrarily without using AJAX to send the data back to the server, which I did by creating a hidden div containing the template of one βrowβ / element in the collection:
Hidden line "Template":
<div id="RowTemplate" style="display: none"> <div class="grid-5-12 clear"> @Html.Hidden("Dates.Index", "REPLACE_ID") @Html.Hidden("Dates[REPLACE_ID].DateID", "REPLACE_ID") @Html.TextBox("Dates[REPLACE_ID].StartDate", "") </div> <div class="grid-5-12"> @Html.TextBox("Dates[REPLACE_ID].EndDate", "") </div> </div>
Then, jQuery is used, which clones the template, provides a random identifier to use for the new line, and now adds the visible cloned line to the containing div above:
jQuery to complete the process:
<script type="text/javascript"> function AddDatesRow() { var tempIndex = Math.random().toString(36).substr(2, 5); var template = $('#RowTemplate'); var insertRow = template.clone(false); insertRow.find('input').each(function(){ //Run replace on each input this.id = this.id.replace('REPLACE_ID', tempIndex); this.name = this.name.replace('REPLACE_ID', tempIndex); this.value = this.value.replace('REPLACE_ID', tempIndex); }); insertRow.show(); $('#DatesBlock').append(insertRow.contents()); //Attach datepicker to new elements $('input[name="Dates['+tempIndex+'].StartDate"]') .datepicker({dateFormat: 'yy-mm-dd' }); $('input[name="Dates['+tempIndex+'].EndDate"]') .datepicker({dateFormat: 'yy-mm-dd' }); } </script>
Example JSFiddle result: http://jsfiddle.net/mdares/7JZh4/