Reputation: 3315
I have a form on a razor view page where I am adding rows dynamically using jQuery. I want to bind the dynamically created fields to an array so that I can browse through the array one at a time and insert them to a table in the database. The problem is the fields appear in the "FormCollection" as individual fields rather than as an array.
Please see the attached image for view page:
jQuery script to add new rows:
$(function() {
var tableRowNum = 1;
$("#add-work-row").click(function () {
tableRowNum++;
var tableRow = "<tr>";
tableRow += "<td><input name='works[" + (tableRowNum - 1) + "].workCover' type='checkbox' class='text' /></td>";
tableRow += "<td><input name='works[" + (tableRowNum - 1) + "].workTitle' type='text' class='text work-title caps' /></td>";
tableRow += "<td><input name='works[" + (tableRowNum - 1) + "].workComposers' type='text' class='text work-composer caps' /></td>";
tableRow += "<td><input name='works[" + (tableRowNum - 1) + "].workPerformances' type='text' class='text work-performances' /></td>";
tableRow += "<td><input name='works[" + (tableRowNum - 1) + "].workDuration' type='text' class='text work-duration input-duration' /></td>";
tableRow += "<td><a href='#' class='delete-row'></a></td>";
tableRow += "</tr>";
$("#worksTable").append(tableRow);
return false;
});
});
The controller action is:
public ActionResult CreateReport(FormCollection form)
{
// works is null?
var works = form["works"];
foreach (var work in works)
{
// Do something
}
return null;
}
Upvotes: 3
Views: 3743
Reputation: 4594
The script for adding new rows should be as follows.
Notice the works
prefix in input field names, it will be used in the MVC controller action.
Also, the worksPerformedCollection
is the same as the name of the property in your viewmodel that contains rows for "Works Performed" section.
The index
(specifically, works.worksPerformedCollection.index
) is used to group inputs per row. It is not needed if tableRowNum
is sequential, but needed if it's not. I am guessing your rows may not be sequentially numbered since you have the functionality in UI to remove rows.
$(function() {
var tableRowNum = 1;
$("#add-work-row").click(function () {
tableRowNum++;
var tableRow = "<tr>";
// Index value for grouping (non-sequential) rows.
tableRow += "<td><input type='hidden' name='works.worksPerformedCollection.index' value=" + (tableRowNum - 1) + " /></td>";
// Checkbox.
tableRow += "<td><input name='works.worksPerformedCollection[" + (tableRowNum - 1) + "].workCover' type='checkbox' class='text' value='true' />";
tableRow += "<input type='hidden' name='works.worksPerformedCollection["+ (tableRowNum - 1) +"].workCover' value='false' /></td>";
tableRow += "<td><input name='works.worksPerformedCollection[" + (tableRowNum - 1) + "].workTitle' type='text' class='text work-title caps' /></td>";
tableRow += "<td><input name='works.worksPerformedCollection[" + (tableRowNum - 1) + "].workComposers' type='text' class='text work-composer caps' /></td>";
tableRow += "<td><input name='works.worksPerformedCollection[" + (tableRowNum - 1) + "].performances' type='text' class='text work-performances' /></td>";
tableRow += "<td><input name='works.worksPerformedCollection[" + (tableRowNum - 1) + "].duration' type='text' class='text work-duration input-duration' /></td>";
tableRow += "<td><a href='#' class='delete-row'></a></td>";
tableRow += "</tr>";
$("#worksTable").append(tableRow);
return false;
});
});
The input fields in the "Performance Details" section should be named as works.performanceDate
, works.performerName
, etc.
On the server side you have the following classes.
public class PerformanceViewModel
{
[Required]
public DateTime PerformanceDate { get; set; }
[Required]
public string PerformerName { get; set; }
// ...
public IEnumerable<WorksPerformedViewModel> WorksPerformedCollection { get; set; }
}
public class WorksPerformedViewModel
{
public bool WorkCover { get; set; }
public string WorkTitle { get; set; }
public string WorkComposers { get; set; }
public int? Performances { get; set; }
public TimeSpan? Duration { get; set; }
}
And in the MVC controller you have this.
[HttpPost]
public ActionResult CreateReport(PerformanceViewModel works)
{
// ...
}
Notice that the controller action accepts the viewmodel as works
- this is the same prefix used when naming input fields on the client side.
Upvotes: 1