83

I have a HTML table as below in my View:

<table id="tblCurrentYear">
    <tr>
        <td>Leave Type</td>
        <td>Leave Taken</td>
        <td>Leave Balance</td>
        <td>Leave Total</td>
    </tr>
    @foreach (var item in Model.LeaveDetailsList)
    {
        <tr>
            <td>@Html.TextBoxFor(m => item.LeaveType, new { width = "100" })</td>
            <td>@Html.TextBoxFor(m => item.LeaveTaken, new { width = "100" })</td>
            <td>@Html.TextBoxFor(m => item.LeaveBalance, new { width = "100" })</td>
            <td>@Html.TextBoxFor(m => item.LeaveTotal, new { width = "100" })</td>
        </tr>
    }
</table>

I want to iterate through all the html table rows and insert the values in ADO.NET DataTable.

Simple speaking, converting HTML Table to ADO.NET DataTable.

How to extract values from HTML Table and insert into ADO.NET DataTable?

The view is based on the following model

public class LeaveBalanceViewModel
{
    public LeaveBalanceViewModel()
    {
        this.EmployeeDetail = new EmployeeDetails();
        this.LeaveBalanceDetail = new LeaveBalanceDetails();
        this.LeaveDetailsList = new List<LeaveBalanceDetails>();
    }
    public EmployeeDetails EmployeeDetail { get; set; }
    public LeaveBalanceDetails LeaveBalanceDetail { get; set; }
    public List<LeaveBalanceDetails> LeaveDetailsList { get; set; }
}
Leonardo Henriques
  • 784
  • 1
  • 7
  • 22
RKh
  • 13,818
  • 46
  • 152
  • 265
  • 3
    Are you looking for get the text box values of table and insert into database or complete html needs to insert ? – Jinesh Jain May 07 '15 at 07:03
  • 7
    Was it not clear from our comments to your last question that you **cannot** use a `foreach` loop to generate controls in a collection. You need a `for` loop or a custom `EditorTemplate` for the model. Your `foreach` loop wont bind to anything. –  May 07 '15 at 07:04
  • 1
    @StephenMuecke But the foreach is working and I able to populate HTML table with it. Once this table is generated, user can change the value in any TextBox and click Save button. On click of Save, I want to pick all TextBox values (row by row) and insert into ADO.NET DataTable. – RKh May 07 '15 at 07:07
  • 4
    NO it is not - you see the values in the view but you cannot bind to anything when you post back. Inspect the html your generating - you have multiple textboxes with `name="LeaveType"`. In order to bind to your collection on post back, the controls need to be `LeaveBalanceDetail[0].LeaveType`, `LeaveBalanceDetail[1].LeaveType` etc. –  May 07 '15 at 07:10
  • 1
    Why do you need to add these into ADO.Net table ? Are you looking to save these into database ? – Mairaj Ahmad May 07 '15 at 07:22
  • 1
    @MairajAhmad yes. I want to Save. – RKh May 07 '15 at 07:23
  • 1
    @StephenMuecke But in this code I am taking item and not LeaveBalanceDetail. Can you please illustrate as an answer? It would definitely help. – RKh May 07 '15 at 07:26
  • 1
    OK will add an answer shortly, but I have edited your question to include the model from your previous question so my answer has some context. I then suggest you delete your previous question. –  May 07 '15 at 07:31

2 Answers2

87

In order to bind to a model on post back, the name attributes of the form controls must match the model properties. Your use of a foreach loop does not generate the correct name attributes. If you inspect the html you will see multiple instances of

<input type="text" name="item.LeaveType" .../>

but in order to bind to your model the controls would need to be

<input type="text" name="LeaveDetailsList[0].LeaveType" .../>
<input type="text" name="LeaveDetailsList[1].LeaveType" .../>

etc. The easiest way to think about this is to consider how you would access the value of a LeaveType property in C# code

var model = new LeaveBalanceViewModel();
// add some LeaveBalanceDetails instances to the LeaveDetailsList property, then access a value
var leaveType = model.LeaveDetailsList[0].LeaveType;

Since your POST method will have a parameter name (say model), just drop the prefix (model) and that's how the name attribute of the control must be. In order to do that you must use either a for loop (the collection must implement IList<T>)

for(int i = 0; i < Model.LeaveDetailsList.Count; i++)
{
    @Html.TextBoxFor(m => m.LeaveDetailsList[i].LeaveType)
    ....
}

or use a custom EditorTemplate (the collection need only implement IEnumerable<T>)

In /Views/Shared/EditorTemplates/LeaveBalanceDetails.cshtml

@model yourAssembly.LeaveBalanceDetails
<tr>
    <td>@Html.TextBoxFor(m => m.LeaveType)</td>
    ....
</tr>

and then in the main view (not in a loop)

<table>
    .... // add headings (preferably in a thead element
    <tbody>
        @Html.EditorFor(m => m.LeaveDetailsList)
    </tbody>
</table>

and finally, in the controller

public ActionResult Edit(LeaveBalanceViewModel model)
{
    // iterate over model.LeaveDetailsList and save the items
}
  • 1
    This explains what must be in the prefix name to make a list detectable by the razor engine. Thanks! ;) – Ian Aug 19 '16 at 10:37
  • 1
    This was very helpful! I'm still having an issue, though - I changed my model type to a List instead of IEnumerable and iterate through it in the View using a For loop instead of For Each. When I submit, though, it gets to the Controller, and my model there is Nothing! Why would it be Nothing? – Andy Mar 16 '17 at 23:05
  • 1
    @Andarta, Without seeing your code I have no idea what mistakes you made. You need to ask a question, showing the relevant details and we will be able to answer it :) –  Mar 16 '17 at 23:32
9

With respect to your requirement, try this

jQuery(document).on("change", ".DDLChoices", function (e) {
    var comma_ChoiceIds = '';
    var comma_ChoicesText = '';
    $('input[class="DDLChoices"]').each(function (e) {
        if (this.checked) {
            comma_ChoiceIds = comma_ChoiceIds + $(this).val() + ',';
            comma_ChoicesText = comma_ChoicesText + $(this).parent('label').parent() + ',';
        }
    });
    $('#ChoiceIds').val(comma_ChoiceIds);
    $('#ChoiceText').val(comma_ChoicesText);
});
@using (Html.BeginForm("Actionname", "Controllername", FormMethod.Post, new { id = "frmChoices" }))
{

    @Html.HiddenFor(m => m.ChoiceText, new { @id = "ChoiceText" })
    @Html.HiddenFor(m => m.ChoiceIds, new { @id = "ChoiceIds" })
    <div class="form-group">
        <div>
            <table>
                <tr>
                    <th>Name</th>
                    <th>Selected</th>
                </tr>
                @foreach (var item in @Model.Choices)
                {
                    <tr>
                        <td> <label>@item.ChoicesText</label>    </td>
                        <td> <input class="DDLChoices" value="@item.ChoiceIds" type="checkbox" /></td>
                    </tr>
                }
            </table>
        </div>
     <input type="button" value="Submit" onclick="return ChoicesPoster.passChoices()"
    </div>
}
jps
  • 20,041
  • 15
  • 75
  • 79
lashja
  • 493
  • 10
  • 21