Reputation: 8362
I have a form which is submitting a collection of TimeItems (a table object), which are then going to be added to the database. The problem I am encountering is that I want to allow the user to add as many or as few TimeItems as they want to the collection before submittal. Currently a TimeItem's input fields consist of a hidden input for a foreign key, a combo box to select an employee, and a basic input box to enter notes in. My view, with javascript:
<div>
<img src="" alt="Add row" id="add-timesheet-row" />
@Using Html.BeginForm("AddToTimesheet", "Project")
@<div>
<input type="hidden" name="TimeItems[0].TaskID" value="@Model.TaskID"/>
<select name="TimeItems[0].EmployeeID">
@For Each emp As SelectListItem In ViewBag.EmployeeID
@<option value="@emp.Value">@emp.Text</option>
Next
</select>
<input type="text" name="TimeItems[0].Notes" value="Test Notes" />
<div id="add-row-target" style="display: none"></div>
<div class="button-submit">
<input type="submit" value="Create" /> |
@Html.ActionLink("Back", "Index")
</div>
</div>
End Using
</div>
<script type="text/javascript">
$(document).ready(function () {
$("#add-timesheet-row").click(function () {
$.get('@Url.Action("RenderTimeItems", New With {.id = Model.TaskID})', function(data) {
$('#add-row-target').before(data);
});
});
});
</script>
What is happening here is that when the "Add row" img is clicked, the javascript returns the RenderTimeItems action, which in turn returns a partial view. That partial view contains the input html again (hidden, combo, and textbox):
<input type="hidden" name="TimeItems[#].TaskID" value="@Model.TaskID"/>
<select name="TimeItems[#].EmployeeID">
@For Each emp As SelectListItem In ViewBag.EmployeeID
@<option value="@emp.Value">@emp.Text</option>
Next
</select>
<input type="text" name="TimeItems[#].Notes" value="Test Notes" />
This html is then added to the original form. The problem with my partial view is that "#" in TimeItems[#]
needs to be an actual number and needs to be incremented each time the user clicks "Add row" for my controller to be able to correctly interpret the collection of TimeItems. Note in my original view the use of TimeItems[0]
for the initial TimeItem, therefore the next one in my partial view should be TimeItems[1]
. How can I keep track of a number, increment it when "Add row" is clicked in my main view, but still access it in the partial view after the call to RenderTimeItems?
When the user submits the form, it goes to the AddToTimesheet action, which consumes the collection and adds each individual TimeItem to the database as in this example like so:
<HttpPost()>
Function AddToTimesheet(ByVal TimeItems() As ppTimeItem) As ActionResult
If TimeItems IsNot Nothing Then
For Each time In TimeItems
db.ppTimeItems.Add(time)
db.SaveChanges()
Next
End If
Return RedirectToAction("Index")
End Function
Furthermore, if I would like to allow the user to delete a TimeItem (that they previously added) before submitting the form, this would remove an index from the collection, which must contain concurrent indices to work properly. i.e. if TimeItems[0]
, TimeItems[1]
, and TimeItems[2]
have been added, and then the user deletes TimeItems[1]
, then TimeItems[2]
will no longer be added correctly because the remaining indices are 0 and 2. Any thoughts?
Upvotes: 0
Views: 260
Reputation: 9881
You will first need to keep track of how many TimeItem rows you have on the page, so you'll need something that is on every row. add-row-target
would work, but I recommend giving the <div>
a class like 'timeItem
':
var numItems = $("#add-row-target").length;
You could continue to return the partial view and in the $.get
callback simply replace the '#' in the return data
with the numItems
.
$.get('@Url.Action("RenderTimeItems", New With {.id = Model.TaskID})', function(data) {
var dataWithNumber = data.replace(/#/g , numItems);
$('#add-row-target').before(dataWithNumber);
});
Optionally, you could pass in the number to the action method that returns the partial
$.get('@Url.Action("RenderTimeItems", New With {.id = Model.TaskID})?number=' + numItems
<input type="hidden" name="TimeItems[@Model.Number].TaskID" value="@Model.TaskID"/>
<select name="TimeItems[@Model.Number].EmployeeID">
@For Each emp As SelectListItem In ViewBag.EmployeeID
@<option value="@emp.Value">@emp.Text</option>
Next
</select>
<input type="text" name="TimeItems[@Model.Number].Notes" value="Test Notes" />
Upvotes: 1