0

I have a ViewModel that holds a list of users. In my view I want to dynamically add and remove users to that list, but also use the @Html.EditorFor helper methods.

public class CreateTenantViewModel
{
    [Required(ErrorMessage = "The name is required")]
    public string Name { get; set; }

    [Display(Name = "Email address")]
    [Required(ErrorMessage = "The email address is required")]
    [EmailAddress(ErrorMessage = "Invalid email address")]
    public string Email { get; set; }

    public List<CreateTenantUserViewModel> Users { get; set; }

    [Required(ErrorMessage = "You need to accept the terms and conditions")]
    public bool TermsAndConditions { get; set; }
}

public class CreateTenantUserViewModel
{
    public string Firstname { get; set; }

    public string Lastname { get; set; }

    [Display(Name = "Email address")]
    [Required(ErrorMessage = "The email address is required")]
    [EmailAddress(ErrorMessage = "Invalid email address")]
    public string Email { get; set; }

    public bool Admin { get; set; } = false;
}

In my view the code for the model creation looks as the following.

<table id="user-table" class="table table-hover">
<thead>
<tr>
    <th>Firstname</th>
    <th>Lastname</th>
    <th>Email</th>
    <th>Admin?</th>
    <th></th>
</tr>
</thead>
<tbody>
<tr class="add-user-row hidden">
    <td>
        <div class="form-group no-margins">
            @Html.LabelFor(model => model.Users[-1].Firstname, new {@class = "sr-only"})
            @Html.EditorFor(model => model.Users[-1].Firstname, new {htmlAttributes = new {@placeholder = Html.DisplayNameFor(model => model.Users[-1].Firstname)}})
            <span class="text-danger">@Html.ValidationMessageFor(model => model.Users[-1].Firstname)</span>
        </div>
    </td>
    <td>
        <div class="form-group no-margins">
            @Html.LabelFor(model => model.Users[-1].Lastname, new {@class = "sr-only"})
            @Html.EditorFor(model => model.Users[-1].Lastname, new {htmlAttributes = new {@placeholder = Html.DisplayNameFor(model => model.Users[-1].Lastname)}})
            <span class="text-danger">@Html.ValidationMessageFor(model => model.Users[-1].Lastname)</span>
        </div>
    </td>
    <td>
        <div class="form-group no-margins">
            @Html.LabelFor(model => model.Users[-1].Email, new {@class = "sr-only"})
            @Html.EditorFor(model => model.Users[-1].Email, new {htmlAttributes = new {@placeholder = Html.DisplayNameFor(model => model.Users[-1].Email)}})
            <span class="text-danger">@Html.ValidationMessageFor(model => model.Users[-1].Email)</span>
        </div>
    </td>
    <td>@Html.CheckBoxFor(model => model.Users[-1].Admin)</td>
    <td><i class="fa fa-trash fa-2x text-danger pointer" onclick="$(this).closest('tr').remove();"></i></td>
</tr>
<tr id="add-user">
    <td colspan="4"><button class="btn btn-primary" onclick="addUserRow(); return false;"><i class="fa fa-plus"></i> Add user</button></td>
</tr>
</tbody>

I am using the table row above as a template and copy is via jquery when a button is clicked:

function addUserRow() {
var clone = $("#user-table tbody > tr:first").clone();
// count up the user number

clone.html(function () {
    return clone.html().replace(new RegExp("-1", "g"), count);
});
clone.find("input:checkbox").iCheck({
    checkboxClass: 'icheckbox_square-green'
});

$("#user-table tr:last").before(clone.removeClass("hidden"));
count++;
}

However it is also possible to remove rows that are in the middle. The problem is that MVC does not seem to bind on all items in the array. It only takes the first item. If there is a gap in the index, e.g. Users[0].Name, Users[3].Name it will only bind to the first item. All items are passed in the request.

I also tried to fix the indexing with jquery but it does not seem to work:

var form = $(this);

$("tr.add-user-row.hidden").remove();

var regExp = new RegExp("(Users.)(\d)", "g");
$("tr.add-user-row")
    .each(function (index) {
        var that = $(this);
        that.html(function () {
            return that.html().replace(regExp, "$1" + index);
        });
        alert(that.html());
    });

// Submit form input
form.submit();

How can I either fix this jquery function, or even better: make MVC bind to all items submitted in the request regardless of their order in the array?

Silthus
  • 1,679
  • 1
  • 23
  • 27
  • 1
    Refer the answers [here](http://stackoverflow.com/questions/29161481/post-a-form-array-without-successful/29161796#29161796) and [here](http://stackoverflow.com/questions/28019793/submit-same-partial-view-called-multiple-times-data-to-controller/28081308#28081308) - the key point is the hidden input for the `Index` property that allows non zero-based, non-consecutive collections to be bound –  Jul 15 '16 at 09:18
  • But how does this handle dynamic deletion of added objects? – Silthus Jul 15 '16 at 09:32
  • As I noted, if you have a ` –  Jul 15 '16 at 09:38
  • But you have other issues as well such as invalid html (you will be generating duplicate `id` attributes) and the hidden row will be submitted, so that should be located outside the `
    ` tags
    –  Jul 15 '16 at 09:40
  • Thanks that solved the problem! – Silthus Jul 15 '16 at 09:48

0 Answers0