Gineer
Gineer

Reputation: 2408

How to create an Edit view for a ViewModel with lists

I'm fairly new to MVC, so I'm sure I'm doing something wrong. I'm trying my best to do it all the MVC way instead of reverting back to just crafting it all by hand in HTML.

Here is my Edit View

@model NotesBoard.Models.RoleViewModel

@{
     ViewBag.Title = "Edit";
}

<h2>Edit</h2>

@using (Html.BeginForm()) {
@Html.ValidationSummary(true)

<fieldset>
    <legend>RoleViewModel</legend>
    @Html.HiddenFor(model => model.Role.Name)

    <div class="editor-label">
        @Html.LabelFor(model => model.Role.Name)
    </div>
    <div class="editor-field">
        @Html.DisplayFor(model => model.Role.Name)
    </div>

    <div class="editor-label">
        @Html.LabelFor(model => model.Role.Users)
    </div>
    <div class="editor-field">
        <table>
            <tr>
                <td class="remove-td">Tick to Remove</td>
                <td></td>
                <td class="add-td">Tick to Add</td>
            </tr>
            <tr>
                <td style="vertical-align:top; margin: 0px;">
                    <table style="margin: 0px;">
                    @foreach (var item in Model.Role.Users)
                    {
                        <tr>
                            <td class="remove-td">
                                @Html.CheckBoxFor(modelItem => item.State)
                            </td>
                            <td>
                                @Html.DisplayFor(modelItem => item.User)
                            </td>
                        </tr>
                    }
                    </table>
                </td>
                <td></td>
                <td style="vertical-align:top; margin:0px;">
                    <table style="margin: 0px;">
                    @foreach (var item in Model.Users)
                    {
                        Boolean RoleUser = Model.Role.Users.Exists(model => model.User == item.User);
                        <tr>
                        @if(RoleUser)
                        {
                            <td class="add-td">
                                @Html.CheckBoxFor(modelItem => item.State, new { disabled=true })
                            </td>
                            <td class="disabled-label">
                                @Html.DisplayFor(modelItem => item.User)
                            </td>
                        }
                        else
                        {
                            <td class="add-td">
                                @Html.CheckBoxFor(modelItem => item.State, new { id=item.User })
                            </td>
                            <td>
                                @Html.DisplayFor(midelItem => item.User)
                            </td>
                        }
                        </tr>
                    }
                    </table>
                </td>
            </tr>
        </table>
    </div>

    <p>
        <input type="submit" value="Save" />
    </p>
</fieldset>
 }

 <div>
     @Html.ActionLink("Back to List", "Index")
 </div>

 @section Scripts {
     @Scripts.Render("~/bundles/jqueryval")
 }

And here is my model:

public class AccountUser
{
    public AccountUser() { }

    public AccountUser(Int32 id, String user)
    {
        Id = id;
        User = user;
    }

    [Key]
    public Int32 Id { get; set; }
    public String User { get; set; }
}

public class AccountUserViewModel : AccountUser
{
    public AccountUserViewModel(Int32 id, String user, Boolean State = false)
    {
        Id = id;
        User = user;
    }

    public Boolean State { get; set; }
}

public class RoleModel
{
    public String Name { get; set; }
    public List<AccountUserViewModel> Users { get; set; }
}

public class RoleViewModel
{
    public RoleModel Role { get; set; }
    public List<AccountUserViewModel> Users { get; set; }
}

When I get the postback from the edit view both the user lists are null.

Here is the Post method in my controller so far:

    [HttpPost]
    [Authorize(Roles = "Admin")]
    public ActionResult Edit(RoleViewModel model)
    {
        if (ModelState.IsValid)
        {
            var data = Request.Form;
            return RedirectToAction("Index");
        }
        return View(model);
    }

If I try to extract the data directly by using the Request.form collection, the checkboxes are all named "item.state". How should I be implementing this view to get descent data back without reverting to basically just crafting the form in plane old HTML myself?

Upvotes: 1

Views: 3879

Answers (1)

Quinton Bernhardt
Quinton Bernhardt

Reputation: 4793

Change the foreach over the lists to FOR loops so that the index can be used in the lamda and rendered correctly by the Razor view:

                @for (int i = 0; i < Model.Users.Count(); i++) {

                {

                    <tr>
                    @if(RoleUser)
                    {
                        <td class="add-td">
                            @Html.CheckBoxFor(modelItem => Model.Users[i].State, new { disabled = true })
                        </td>
                        <td class="disabled-label">
                            @Html.DisplayFor(modelItem => Model.Users[i].User)
                        </td>
                   ...
                   ...

The model binder will then bind the data to the lists correctly.

Check out this SO answer: https://stackoverflow.com/a/6585642/1099260

Upvotes: 2

Related Questions