0

I am rather new to MVC so please bear with me.

My goal at hand is to POST a checkboxfor html helper that contains the property called "Verified" to my controller using Ajax. I did mounds of research and yielded this post which is very similar, however it still does not reach the controller and instead I receive a 505 error. checkboxfor Ajax link. This post didn't show how the controller handled the data being passed to the and I am lead to believe that may be my issue but I am not certain. Or it may be something with the foreach block doing some weird stuff.(Side note on the table, we use datatables.net library for table formatting). Below is my code...

Model (comes directly from the EF entity class and is brought in via Meta Data)

namespace Entities
{
public class FPDrinkingWater : AccessibleRecord<FPDrinkingWater>
{
    public FPDrinkingWater();

    public bool Approved { get; set; }
    public string ApprovedBy { get; set; }
    public DateTime? ApprovedDate { get; set; }
    public string Clorinated { get; set; }
    public DateTime? CollectionDate { get; set; }
    public TimeSpan? CollectionTime { get; set; }
    public string Collectors { get; set; }
    public string Comments { get; set; }
    public override string CreatedBy { get; set; }
    public override DateTime CreatedDate { get; set; }
    public bool Deleted { get; set; }
    public bool EC { get; set; }
    public override int Id { get; set; }
    public string Location { get; set; }
    public bool RAG { get; set; }
    //
    // Summary:
    //     Drinking water record
    public override string RecordName { get; }
    public short? Replicate { get; set; }
    public virtual SampleLog SampleLog { get; set; }
    public int SID { get; set; }
    public bool SWAT_LAB { get; set; }
    public bool TIMS { get; set; }
    public override string UpdatedBy { get; set; }
    public override DateTime? UpdatedDate { get; set; }
    public bool Verified { get; set; }
    public string VerifiedBy { get; set; }
    public DateTime? VerifiedDate { get; set; }

    public override void UpdateTo(FPDrinkingWater entity);

    protected override void ParameterChecks();
    }
}

Controller action to populate view:

//unverified Drinking Water log
    [OpenAction]
    public async Task<ActionResult> UnverifiedDrinkingWaterLog(AlertModel alert)
    {
        //return partial Drinking water log view
        return await UnverifiedDataTableAsync<FPDrinkingWater>(alert);
    }

Definition of UnverifiedDataTableAsync:

 //Unverified data table
    [OpenAction]
    public async Task<ActionResult> UnverifiedDataTableAsync<TEntity>(AlertModel alert, string viewName = null) where TEntity : class
    {
        // get data

        var data = (from a in await LimsManager.AllAsync<TEntity>()
                    select a).ToList();
        //LimsManager.AllAsync returns the entire context contents  of the Entity in a List then the view tests if `Verified` is not populated.

        // create sample log model with alert

        var response = new TableModel<TEntity>
        {
            Alert = alert,
            Data = data
        };

        // return partial data view
        return PartialView(viewName ?? $"_Unverified{typeof(TEntity).Name}Table", response);
    }

Controller update action:

    [HttpPost]
    [ValidateAntiForgeryToken]
    public async Task<ActionResult> VerifyDrinkingWater(FPDrinkingWater drinkingWater, string id )
    {
        //create alert model
        var response = new AlertModel();

        //check model state
        if(ModelState.IsValid)
        {
            //add the verifier to the DrinkingWater record
            drinkingWater.Verified = true;
            drinkingWater.VerifiedBy = User.Identity.Name;
            drinkingWater.VerifiedDate = DateTime.Now;

            //update the drinking water with the new verified information
            var result = await LimsManager.UpdateAsync(drinkingWater);

            //check update result
            if(result.Succeeded)
            {
                response.AddInfo($"All SID's successfully verified in the database.", BSContextualTypes.success);
            }
            else
            {
                //add failure to alert
                response.AddInfo($"Unable to verify SID's in database.", BSContextualTypes.warning);
            }
        }
        else
        {
            InvalidState(response);
        }
        return await UnverifiedDrinkingWaterLog(response);
    }

The view should iterate over the context of the entity and building the table for each record it finds that is not verified:

@model MyProgram.Application.TableModel<Entities.FPDrinkingWater>
@{
Layout = null;

var insertionMode = InsertionMode.Replace;
var fail = "displayFailure";
var target = "UnverifiedDrinkingWaterContent";
var ajax = "UnverifiedDrinkingWaterLogLoader";
var verify = Html.UserHasClaim("FP/DrinkingWater/VerifyDrinkingWater");


}

<br />

@if (Model != null && Model.Alert != null && Model.Alert.Message != null)
{
    @Html.Alert(Model.Alert)
}

@MyHelper.Loader(ajax)
<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>
   @if (Model.Data.Any(v => !v.Verified))
   {
   foreach (var log in Model.Data.Where(v => !v.Verified))
   {
    <tr data-record-id="@log.Id">
        <td>@log.SID</td>
        <td>@log.Location</td>
        <td>@log.Replicate</td>
        <td>@log.CollectionDate.Value.ToShortDateString()</td>
        <td>@log.CollectionTime</td>
        <td>@log.Collectors</td>
        <td>@log.Clorinated</td>
        <td>@log.Comments</td>
        <td>@Html.CheckBoxFor(m => log.Verified, new { data_url = Url.Action("VerifyDrinkingWater", "DrinkingWater"), id = "mycheckbox", @class = "toggle" })</td>
    </tr>
     }
     }
   else
   {
    <tr>
        <td colspan="@Html.ColumnCount(3)"><em>No Drinking Water data to verify.</em></td>
    </tr>
   }
    </tbody>
</table>


<hr />


@if (Model.Data.Count > 0)
{ 
    <script>

    $(document).ready(function () {
        makeDataTable('UnverifiedDrinkingWaterTable');
        $(function () {
            $('.toggle').change(function () {
                var self = $(this);
                var url = self.data('url');
                var id = self.attr('id');
                var value = self.prop(':checked');

                $.ajax({
                    url: url,
                    data: { id: id },
                    type: 'POST',
                    success: function (result) {
                        alert(response)
                    }

                });
            });

        });
    });

    </script>
}

Generated HTML:

<tr data-record-id="3" role="row" class="odd">
        <td>5</td>
        <td>Location</td>
        <td>1</td>
        <td>3/31/1997</td>
        <td>18:00:00</td>
        <td>YY</td>
        <td>Y</td>
        <td></td>
        <td><input class="toggle" data-url="/FP/DrinkingWater/VerifyDrinkingWater" data-val="true" data-val-required="The Verified field is required." id="mycheckbox" name="log.Verified" type="checkbox" value="true"><input name="log.Verified" type="hidden" value="false"></td>
    </tr>

And that data-record-id matches the ones in the database.

Braden
  • 135
  • 1
  • 11
  • Your `CheckBoxFor()` dos not make sense in a `foreach` loop (refer [this answer](https://stackoverflow.com/questions/30094047/post-an-html-table-to-ado-net-datatable/30094943#30094943) for a bit more understanding)), and you are also generating invalid html because of the duplicate `id` attributes –  Aug 11 '18 at 03:31
  • Then in the script you use `var id = self.attr('id');` which always returns `"mycheckbox"`, and you never actually send anything related to the `bool` property (i.e. `true` or `false`) to the POST method. And then your `VerifyDrinkingWater()` contains a `FPDrinkingWater` parameter which will not be bound (and you have not shown that model anyway) - the only thing that will be bound is the `string `id` parameter which will always have a value of "mycheckbox". Its not clear what it is you want to sens or want to bind to. –  Aug 11 '18 at 03:36
  • Maybe there's a better approach to accomplish this. Ultimately I'd like to update the record by clicking a checkbox but I sense that this may be a lot harder than I anticipated. I will update my original post with the full model and the definition of `DataTableAsync`. – Braden Aug 11 '18 at 15:26
  • I'll update the post with the generated HTML – Braden Aug 11 '18 at 16:01
  • Actually its a lot easier than you think. But its still not clear what you want to update. I assume you want to just update the `Verified` property of your model based on of the checkbox is checked or not? (and are you wanting to set it to `false` if you uncheck the checkbox? –  Aug 12 '18 at 04:49
  • Correct. The reason why I want to set it up this way is due to the fact that there will be many records to verify at once. Normally I would think to send the independent record to a new view, similar to an update, with read-only fields except for the Verified field...but with so many records it would be faster for the user to scan the table and verify the properties then check the box. I did, however, change the table to using a for loop but the Ajax call is now producing some really strange results. – Braden Aug 12 '18 at 18:20
  • It's almost as if the Ajax is called when the table is loaded and not when the check box is clicked – Braden Aug 12 '18 at 18:20
  • If you are wanting to update multiple records in the collection, then why are you (incorrectly) posting back a value for just one record. Suggest you start by reading [Pass List of Checkboxes into View and Pull out IEnumerable](https://stackoverflow.com/questions/29542107/pass-list-of-checkboxes-into-view-and-pull-out-ienumerable/29554416#29554416). And its not even clear why you need ajax rather that a normal submit, but if you do, then you just use `data: $(yourform).serialize()` to correctly serialize the data. –  Aug 13 '18 at 00:58
  • It's probably because I'm thinking of the data as individual records instead of a collection of records. My apologies for being dense in this matter as I've only been working with ASP.NE MVC for less than a year – Braden Aug 13 '18 at 01:34
  • @StephenMuecke how is the model Posted to the controller when there's a collection? The links you posted earlier just say to use a `` but this doesn't say what controller or action to pass the data to. – Braden Aug 14 '18 at 22:16
  • You specify that in the `
    ` tag (e.g. `@using (Html.BeginForm()) { ... }` will post to a method with the same name as the GET method, or `@using (Html.BeginForm("X", "Y" )) { ... }` will submit to the `X()` method in `YController`)
    –  Aug 14 '18 at 22:19
  • What about an `Ajax.BeginForm` or `Ajax.ActionLink`? – Braden Aug 14 '18 at 22:31
  • Exactly the same for `Ajax.BeginForm()` (but using `Ajax.ActionLink()` makes no sense - that generates a link that makes a GET). But why use ajax in this case - if the user selects all the items they want to be verified, why would they want to stay on the same page? –  Aug 14 '18 at 22:34
  • So I can place the entire table into the `@using (Html.BeginForm()) {....}` and then the button directs it to the controller and action? Placing the table within is acceptable? The idea is to remain on the same page and just refresh the table. Or is there a better option? – Braden Aug 14 '18 at 22:37
  • Yes (and refer [Pass List of Checkboxes into View and Pull out IEnumerable](https://stackoverflow.com/questions/29542107/pass-list-of-checkboxes-into-view-and-pull-out-ienumerable/29554416#29554416) - you will just have a `` inside the ``).
    –  Aug 14 '18 at 22:41
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/178039/discussion-between-braden-and-stephen-muecke). – Braden Aug 14 '18 at 22:44

0 Answers0