kez kez - 22 days ago 5
C# Question

Dynamic Table row with Default Row When Model Null

I'm trying to create a table that can populate dynamic rows, also I'm trying to populate single default raw when model is null

Model properties

public class UsrViewModel
{
public IEnumerable<User> SysUser { get; set; }
}

Public class User
{
public int ID {get; set:}
public string user {get; set:}
..
}


Controller methods

[HttpGet]
public ActionResult Users()
{
try
{

IList<Users> listofusers = ..

var model = new UsrViewModel
{
SysUser = listofsensors
};

return View(model);
}
catch (Exception ex)
{
throw ex;
}
}

[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Users(IEnumerable<UsrViewModel> model)
{
try
{
...

return View();
}
catch (Exception ex)
{
throw ex;
}
}


View page

@model ProjectName.ViewModels.UsrViewModel
@{
}
<div class="content-page">

<div class="content">
<div class="container">
<div class="row">
<div class="col-xs-12">
@using (Html.BeginCollectionItem("Users", "Sample", FormMethod.Post))
{
<table class="table table-striped table-bordered" id="SysUser ">
<tr>
...
</tr>

@if (Model != null)
{
for (int i = 0; i < Model.SysUser.Count; i++)
{
<tr>
<td>
[i]
</td>
<td>
@Html.TextBoxFor(m => m.UsrViewModel[i].user , new { @type = "text", @class = "", @placeholder = "user Name", @required = "" })
@Html.ValidationMessageFor(m => m.UsrViewModel[i].user, "", new { @class = "text-danger" })
</td>
....

</tr>
}
}

</table>

</div>
<div class="col-sm-offset-1 col-sm-11">
<button type="submit" class="btn btn-purple waves-effect waves-light btn-wd-130">Save</button>
<button type="submit" class="btn btn-default waves-effect waves-light btn-wd-130">Clear</button>
</div>
}
</div>
<!-- end row -->

</div>
</div>

</div>
</div>


But this is not populating single default row when model is null , and can't add new rows dynamically. what should I change here?.

Answer

You using the BeginCollectionItem() method the wrong way. It needs to be applied to a single object. Its purpose is to modify the html to add collection indexers (based on a Guid) and add a hidden input for the indexer, which allows you to generate the html for objects in a collection and allow you to dynamically add (and remove) items to the collection.

Start by creating a partial view (say) _User.cshtml that generates a table row for a user

@model User
<tr>
    @using (Html.BeginCollectionItem("SysUser")) // the parameter is the name of your collection property
    {
        <td>
            @Html.HiddenFor(m => m.ID)
            @Html.TextBoxFor(m => m.user)
            @Html.ValidationMessageFor(m => m.user)
        <td>
        <td>
            .... // form controls for other properties of user
        </td>
    }
</tr>

Then in the main view, use Partial() to generate the html for each User in the collection

@model UsrViewModel
@using (Html.BeginForm())
{
    <table>
        <thead>....</thead>
        <tbody id="users">
            @foreach (var user in Model.SysUser)
            {
                @Html.Partial("_User", user)
            }
        </tbody>
    </table>
    <input type="submit" value="save" />
}

The only reason to use the BeginCollectionItem() is to be able to dynamically add and/or remove items from the collection in the view, so assuming you have an 'Add' button, you would handle its .click() event to call a server method that returns a partial view for a new User and append it to the table, for example

<button id="add" type="button">Add New User</button>

var url = '@Url.Action("NewUser")';
var users = $('#users');
$('#add').click(function () {
    $.get(url, function (html) {
        users.append(html);
    });
});

when your controller method is

public PartialViewResult NewUser()
{
    return PartialView("_User, new User());
}

You might also include a 'Delete' button in each row, so that you can handle its click event to call a server method that deletes the user from the database, and in the ajax success callback, remove the row from the table.