Braden
Braden

Reputation: 135

Asp.Net MVC 5 Checkbox states do not remain the same for records in a list that are not checked on POST

I have a view that is displaying records on a table with checkboxes for the user to select and update. However, if I have 3 records in the table, Data[0], Data[1], Data[2] and I select only the checkbox for Data[0] and POST it to the controller, then when the screen is returned Data[1] checkbox now has the state of Data[0] that was submitted (true). However the database shows the checkbox value is 0 (false) for Data[1]. My only guess is something with the model binding or I would need to persist the checkbox states some how.

Model

public class DrinkingWaterModel 
{
    //contains all properties of the FPDrinkingWater Entity 
    public List<FPDrinkingWater> Data { get; set; } 

    public AlertModel SuccessAlert { get; set; }

    public AlertModel FailureAlert { get; set; }
}

GET Method

public async Task<ActionResult> UnverifiedDrinkingWaterLog(AlertModel 
successAlert, AlertModel failureAlert)
    {
        //get unverified data from the db
        var data = (from s in await Manager.Store.GetAllAsync<FPDrinkingWater>()
                   where s.Verified.Equals(false)
                   select s).ToList();

        //fill the model
        DrinkingWaterModel model = new DrinkingWaterModel
        {
            SuccessAlert = successAlert,
            FailureAlert = failureAlert,
            Data = data
        };

        return PartialView("_UnverifiedFPDrinkingWaterTable", model);
    }

The View

@model MyApplication.Areas.FP.Models.DrinkingWaterModel
@{
Layout = null;
}
<div>
    @Html.AntiForgeryToken()
    <table id="UnverifiedDrinkingWaterTable" class="table table-hover">
        <thead>
            <tr>
                <th>@Html.LabelFor(m => m.Data.FirstOrDefault().SID)</th>
                <th>@Html.LabelFor(m => m.Data.FirstOrDefault().Location)</th>
                <th>@Html.LabelFor(m => m.Data.FirstOrDefault().Replicate)</th>
                <th>@Html.LabelFor(m => m.Data.FirstOrDefault().CollectionDate)</th>
                <th>@Html.LabelFor(m => m.Data.FirstOrDefault().CollectionTime)</th>
                <th>@Html.LabelFor(m => m.Data.FirstOrDefault().Collectors)</th>
                <th>@Html.LabelFor(m => m.Data.FirstOrDefault().Clorinated)</th>
                <th>@Html.LabelFor(m => m.Data.FirstOrDefault().Comments)</th>
                <th>@Html.LabelFor(m => m.Data.FirstOrDefault().Verified)</th>
            </tr>
        </thead>

        <tbody>
            @for (int i = 0; i < Model.Data.Count(); i++)
            {
                <tr>
                    @Html.HiddenFor(m => m.Data[i].Id)
                    @Html.HiddenFor(m => m.Data[i].SID)
                    <td>@Html.DisplayFor(m => m.Data[i].SID)</td>
                    @Html.HiddenFor(m => m.Data[i].Location)
                    <td>@Html.DisplayFor(m => m.Data[i].Location)</td>
                    @Html.HiddenFor(m => m.Data[i].Replicate)
                    <td>@Html.DisplayFor(m => m.Data[i].Replicate)</td>
                    @Html.HiddenFor(m => m.Data[i].CollectionDate)
                    <td>@Html.DisplayFor(m => m.Data[i].CollectionDate)</td>
                    @Html.HiddenFor(m => m.Data[i].CollectionTime)
                    <td>@Html.DisplayFor(m => m.Data[i].CollectionTime)</td>
                    @Html.HiddenFor(m => m.Data[i].Collectors)
                    <td>@Html.DisplayFor(m => m.Data[i].Collectors)</td>
                    @Html.HiddenFor(m => m.Data[i].Clorinated)
                    <td>@Html.DisplayFor(m => m.Data[i].Clorinated)</td>
                    @Html.HiddenFor(m => m.Data[i].Comments)
                    <td>@Html.DisplayFor(m => m.Data[i].Comments)</td>
                    <td>@Html.EditorFor(v => v.Data[i].Verified) </td>
                </tr>
            }

            @if (Model.Data.Count() == 0)
            {
                <tr>
                    <td colspan="@Html.ColumnCount(9)"><em>No Drinking Water data to verify.</em></td>
                </tr>
            }

        </tbody>
    </table>

@if (verify)
{
<button type="submit" class="btn btn-primary" data-loading-text="Verifying...">Verify</button>
}
</div>
<script>
$(document).ready(function () {
        makeDataTable('UnverifiedDrinkingWaterTable')
    });
    $('#RefreshDrinkingWater').click();



</script>

The POST method

[HttpPost]
[ValidateAntiForgeryToken]
public async Task<ActionResult> VerifyDrinkingWater([System.Web.Http.FromBody] DrinkingWaterModel model)
    {
        var successAlert = new AlertModel();
        var failureAlert = new AlertModel();

        if (ModelState.IsValid)
        {
            List<string> successes = new List<string>();
            List<string> failures = new List<string>();


            foreach (var verifiable in model.Data)
            {
                if (verifiable.Verified != false)
                {
                    verifiable.Verified = true;
                    verifiable.VerifiedDate = DateTime.Now;
                    verifiable.VerifiedBy = User.Identity.Name;  
                    var result = await Manager.VerifyAsync(verifiable);
                }
             }
        }
        else
        {
            InvalidState(failureAlert);
        }
        //return the GET method to update and refresh the table
        return await UnverifiedDrinkingWaterLog(successAlert, failureAlert);
    }

Upvotes: 0

Views: 195

Answers (1)

Justin Helgerson
Justin Helgerson

Reputation: 25551

The value isn't updating because it's being read by ModelState and not the model. Check out some related questions to get a better idea of why this feature exists. The short version is that typically you only return the exact same page from a POST if there is an error that needs to be corrected. By reading from the ModelState you give the user the benefit of keeping their previous input.

Two options to resolve this problem:

  1. Call ModelState.Clear(); in your HttpPost action before returning the view. Personally I would not recommend this, but it is an option.
  2. If the ModelState is valid, follow the Post -> Redirect -> Get pattern and call RedirectToAction to your original HttpGet method. If the ModelState is not valid, then do what you're doing so you can show an error message and keep the user input.

Upvotes: 1

Related Questions