1

As per this post , in order for ASP.NET MVC to bind to a model on post back, the name attributes of the form controls must match the model properties.

Now as per the jQueryDataTable documentation , what we could set the id of elemnts by using DT_RowId.

Is there a way we could add name attaribute to jQueryDataTable.

Data I am working with at the moment.

"data":[
        "ID":10801,"Name":"SelectedContainers_C05","Description":null,"Tag":null,"Container_ID":10,"IsSelected":false,"DT_RowId":10801},
        {"ID":10802,"Name":"SelectedContainers_C06","Description":null,"Tag":null,"Container_ID":10,"IsSelected":false,"DT_RowId":10802},
        {"ID":10803,"Name":"SelectedContainers_C07","Description":null,"Tag":null,"Container_ID":9,"IsSelected":false,"DT_RowId":10803},
        {"ID":10804,"Name":"SelectedContainers_C08","Description":null,"Tag":null,"Container_ID":10,"IsSelected":false,"DT_RowId":10804},
        {"ID":10805,"Name":"SelectedContainers_C09","Description":null,"Tag":null,"Container_ID":10,"IsSelected":false,"DT_RowId":10805}
}

And this is my javascript to bind json data to View.

Dashboard.Helpers.elementExists('#ContainersTable', function () {
    $(this).DataTable(
        {
            "ajax": {
                "url": "/API/Loaddata",
                "type": "GET",
                "datatype": "json"
            },
            "columns": [
                {
                    "data": "IsSelected",
                    "render": function (data, type, row) {
                        if (type === 'display') {
                            return '<input type="checkbox" class="editor-active">';
                            console.log(data);
                        }
                        return data;
                    },
                    "className": "dt-body-center"
                   // "autoWidth": true
                },
                { "data": "Name", "autoWidth": true }
            ],
            "rowCallback": function (row, data) {
                // Set the checked state of the checkbox in the table
                $('input.editor-active', row).prop('checked', data.IsSelected == 1);
            }
        }
    );

    $(this)
        .removeClass('display')
        .addClass('table table-striped table-bordered');
});

I tried setting the name on row call back,but it is not working.

"rowCallback": function (row, data) {
                // Set the checked state of the checkbox in the table
                $('input.editor-active', row).prop('checked', data.IsSelected == 1);
                $('input.editor-active', row).prop('name', data.Id);
            }

Update

I have this View Model which holds Enums, DateTime and collection of containers. Now when I click submit I need all of them posted .

I could use jQuery to go to individual element and then get there value and post them back. But if I could use ASP.NET MVC biding by adding name to container collection..

ViewModel

 public class ReportViewModel
    {
        public DateTime StartTime { get; set; }
        public DateTime EndTime { get; set; }
        public ESampleTime SampleTime { get; set; }
        public Mesaurands Measurands { get; set; }
        public IEnumerable<ContainersViewModel> Containers { get; set; }
    }

  public class ContainersViewModel
    {
        public int ID { get; set; }
        public string Name { get; set; }
        public string Description { get; set; }
        public string Tag { get; set; }
        public int? Container_ID { get; set; }

        public bool IsSelected { get; set; }

        public int DT_RowId { get; set; }
    }

The controller method this will post back to is

[HttpPost]
public ActionResult Index(ReportViewModel vm)
{
    ....
}
Simsons
  • 12,295
  • 42
  • 153
  • 269
  • From your previous question, you were posting the data back using ajax, so its not necessary to add the `name` attributes - you could just build an array of values to post back. In any case, the only input you have is the checkboxes (which would need `name="[#].IsSelected"` - where `#` is the zero based indexer) which on its own would make no sense - what other values are you wanting to post back? –  Jan 23 '18 at 00:43
  • Hi @StephenMuecke, Updated the question.. – Simsons Jan 23 '18 at 00:58
  • Can you try same inside `columns.render` ? – Sagar Jan 23 '18 at 01:01
  • Based on your models, it would actually need to be `name="Circuits[#].IsSelected"` assuming your binding to `ReportViewModel` in the POST method. And you would also need to add `value="true"`. But then you also need inputs for all the other properties with respective `name` and `value` attributes. –  Jan 23 '18 at 01:01
  • But it would be crazy to add all the inputs in order to post back all the values of all the properties (and a malicious user could just alter them anyway). Why not just use (say) `` and add a `value` attribute (equal to the `ID` property value) in the `rowCallback` function. The the POST method parameter would be just `IEnumerable id` which would contain the ID's of all the checked checkboxes –  Jan 23 '18 at 01:05
  • As a side note, you do not really need the additional `DT_RowId` property to duplicate the `ID` property - you can use the [rowId](https://datatables.net/reference/option/rowId) property to define the ID property of your model. –  Jan 23 '18 at 01:07
  • To set the `value` attribute based on the ID, it should be just `$('input.editor-active', row).val(data.ID);` –  Jan 23 '18 at 01:11
  • @StephenMuecke,Setting value works but Why name = "id" in ? This is just creating for all the elements. – Simsons Jan 23 '18 at 01:15
  • 1
    Because that would bind to a parameter in the post method which is `int[] Id`, and since unchecked checkboxes do not submit a value, your array will be populated with only the ID's of the checked checkboxes –  Jan 23 '18 at 01:18
  • This is my POST method, [HttpPost] public ActionResult Index(ReportViewModel vm) { // Do something here } . The List of containers is still empty when click submit. – Simsons Jan 23 '18 at 01:22
  • 1
    Based on my comments above, your method needs to be `[HttpPost]public ActionResult Index(int[] id)`. While it would be possible to generate all the inputs for each property in each item in the collection in order to bind to `ReportViewModel `, it really makes no sense to do so –  Jan 23 '18 at 01:27
  • Thanks , This works for me as I was still unsure how model binding works. In this case , it look for all the controls named id and tries to post to my controller which takes int[] id ( so matching parameters , or class properties with UI element names). If you could explain this in your answer may help many , and I will mark them as an answer as it solves my root problem.. – Simsons Jan 23 '18 at 01:45
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/163674/discussion-between-stephen-muecke-and-simsons). –  Jan 23 '18 at 02:21

1 Answers1

1

In order in post back you your ReportViewModel, your input for the IsSelected property would need to be

<input type="checkbox" name="Containers[#].IsSelected" value="true" .... />

where # is the zero based indexer for the item in the collection.

Ideally you also need a <input type="hidden" name="Containers[#].IsSelected" value="false" /> input to ensure a value is posted back (unchecked checkboxes do not submit a value, so false is posted, and in the case of it being checked, both true and false are submitted, and the DefaultModelBinder only binds the first result).

But just having that property bound alone makes little sense, so you would also need hidden inputs for all the other properties in the collection, e.g.

<input type="hidden" name="Containers[#].ID" value="10801" />
<input type="hidden" name="Containers[#].Name" value="SelectedContainers_C05" />
....

That would not only be degrading performance (generating a lot of extra html and posting it all back to the server unchanged), you risk a malicious user altering the data which may have consequences depending on what your POST method code is doing.

A simpler solution would be to just post back an array of the selected ID values. For a collection of simple values, generating the collection indexer is not required. It is only necessary to name the all inputs the same, and match that to a parameter with the same name which is type of IEnumerable<int>

Change the render callback to add a name attribute

"render": function (data, type, row) {
    if (type === 'display') {
        return '<input type="checkbox" name="id" class="editor-active">';
    }
    ....

and modify the rowCallback callback to set the value

"rowCallback": function (row, data) {
    $('input.editor-active', row).prop('checked', data.IsSelected).val(data.ID); 
}

And modify the POST method to

[HttpPost]
public ActionResult Index(IEnumerable<int> id)
{
    ....
}

And the id parameter will be bound with all the ID values of the checked checkboxes.

Note you could also include the ReportViewModel vm parameter if your form also includes form controls for the other properties of ReportViewModel.

If you really did want the Containers property of ReportViewModel to be fully bound in the POST method, then one option would be to modify the render callback to generate all the inputs with the correct name and value attributes, for example

"render": function (data, type, row, meta) {
    if (type === 'display') {
        var baseName = 'Containers[' + meta.row + '].'; // Containers[0].
        // Html for ID property
        var idName = baseName + 'ID'; // Containers[0].ID
        var idValue = data.ID; // 10801
        var idHtml = '<input type="checkbox" name="' + IdName + '" value="' + idValue + '" />';
        // Html for Name property
        var nameName = baseName + 'Name'; // Containers[0].Name
        var nameValue = data.Name; // SelectedContainers_C05
        var nameHtml = '<input type="hidden" name="' + nameName + '" value="' + nameValue + '" />';
        .... // etc for all inputs

        return idHtml + nameHtml + .... ;
    }
    ....