-1

This must be the easy one but I've been exploring for some days about how to change a row value clicking on a CheckBox. What I want is to change the value of a column clicking on the CheckBox and keep the CheckBox checked if it is true. Otherwise it will remain unchecked if false. Below is a screenshot and when Public Post column value of a row is 'No', then the CheckBox will remain unchecked. And when I check the CheckBox, then vice-verse means it will remain checked and value will be 'Yes'.

enter image description here

I am not sure how to do it exactly and seen some of the problems or links that deals with this situation:

ASP.NET MVC submitting form on checkbox click

How can I get CheckBox to maintain checked state across postback in ASP.NET MVC?

But I would like to have some more advanced ideas to implement it as it is totally different from ASP.NET Web Form. I would appreciate if experts share some ideas or helpful links to understand. Thanks.

By the way, following is the class that I am working with. I am not sure how to handle the postback from controller and so far following is done using Ajax. But it is not working or updating:

Model:

public class Product
{
    [Key]
    public int Id { get; set; }
    [Required(ErrorMessage = "Details can't be blank")]
    [DataType(DataType.Text)]
    public string Name{ get; set; }
    public string Date_Posted { get; set; }
    public string Time_Posted { get; set; }
    public string Time_Edited { get; set; }
    public string Date_Edited { get; set; }
    public string Public { get; set; }
    public double Price { get; set; }
    public int User_Id { get; set; }
    public bool Status { get; set; } //This is bit column that I am trying to change and not sure how to handle this from controller
    public List<Product> allLists;
    public IEnumerable<Product> Items;
    public Pager Pager;
}

Controller - HomeController.cs:

    [HttpPost]
    public ActionResult CheckValue(int id)
    {
        MainDbContext db = new MainDbContext();
        List result = db.Product.Find(id);

        if (ModelState.IsValid)
        {
            result.Public = "Yes";
            result.Status = true;

            db.Entry(result).State = EntityState.Modified;
            db.SaveChanges();
        }

        return View(db.Lists.ToList());
    }

View - Home/Index:

@model OurFirstWebApp.Models.Product
@{
    ViewBag.Title = "Index";
    var username = User.Identity.Name;
}

<script src="~/Scripts/jquery-1.10.2.min.js"></script>
<script language="javascript">
$(function () {
    $('.toggle').change(function () {
        //alert("Hello");
        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 (response) {
                alert(response);
                $("#divData").load(window.location + " #divData");
            }
        });
    });
});

<div id="divData">
<table class="table table-bordered table-condensed" id="data">
    <thead>
        <tr>
            <th style="text-align:center;">ID</th>
            <th style="text-align:center;">Products</th>
            <th style="text-align:center;">Time Posted</th>
            <th style="text-align:center;">Time Edited</th>
            @if (@User.Identity.IsAuthenticated)
            {
                <th style="text-align:center;">Status</th>
                <th style="text-align:center;">Edit</th>
                <th style="text-align:center;">Delete</th>
                <th style="text-align:center;">Public Post</th>
            }
        </tr>
    </thead>
    <tbody>
        @foreach (var item in Model.Items)
        {
            <tr>
                <td id="ID" style="text-align: center;">@Html.DisplayFor(modelItem => item.Id)</td>
                <td style="text-align: center;">@Html.DisplayFor(modelItem => item.Name)</td>
                <td style="text-align: center;">@Html.DisplayFor(modelItem => item.Time_Posted)</td>
                <td style="text-align: center;">@Html.DisplayFor(modelItem => item.Time_Edited)</td>

                @if (@User.Identity.IsAuthenticated)
                {
                    <td style="text-align: center;">@Html.CheckBoxFor(modelItem => item.Status, new { id = item.Id, @class = "toggle", data_url = Url.Action("CheckValue", "Home") })</td>
                    <td style="text-align: center;"><a href="@Url.Action("Edit", "Auth")/@Html.DisplayFor(modelItem => item.Id)">Edit</a></td>
                    <td style="text-align: center;"><a href="@Url.Action("Delete", "Auth")/@Html.DisplayFor(modelItem => item.Id)">Delete</a></td>
                    <td id="chngData" style="text-align: center;">@Html.DisplayFor(modelItem => item.Public)</td>

                }
            </tr>
        }
   </tbody>
</table>
Community
  • 1
  • 1
  • Add a onclick script doing form.submit on each checkbox? – Allan S. Hansen Jun 13 '16 at 06:09
  • But I've to change the database row value from controller. Then how will I handle this? – DOTNET Rocker Jun 13 '16 at 06:11
  • Put the id clicked into a hidden field. Or you could use ajax submitting only the relevant part of the post to a new controller method or ...... – Allan S. Hansen Jun 13 '16 at 06:19
  • Show the code you have tried, including your controller methods and view. If the view has been generated correctly using an `EditorTemplate` or `for` loop, this will all be handled out of the box. –  Jun 13 '16 at 06:28
  • Hello Stephen Muecke! Please never mind. I can update post with the view but I am not sure how to handle the post back from the controller. Still trying to do so. Just expecting a sample if possible. – DOTNET Rocker Jun 13 '16 at 10:37
  • @DOTNETRocker, No one can add an answer unless you show your code. –  Jun 13 '16 at 11:48
  • @Stephen I've updated the post with codes and not sure why it is not working. I've used Ajax to do so. I would appreciate if you look into it and if there is anything missing. – DOTNET Rocker Jun 15 '16 at 12:42
  • Are you wanting to update the database each time you click on a checkbox? - So if the value of `Public` is initially "Yes" then the checkbox should be initially checked, and then if you uncheck it, you want to immediately update the database to change the value of `Public` to "No" and update the view? –  Jun 15 '16 at 13:00
  • Yes. Exactly. That is what I am trying to do. In the code, I passed the Id to update the specific row but seems like it is not done. – DOTNET Rocker Jun 15 '16 at 13:11
  • It's late here, but I will add an answer in the morning showing how to do this. But I think you have a fundamental problem have 2 properties for the same thing. You only need one database property (say) `bool IsPublic` (the name `Status` makes no sense for a `bool`) and then for display purposes, you can always use a DisplayTemplate to render "Yes" or "No" or have a `public string Public { get { return IsPublic ? "Yes" : "No"; } }` property. –  Jun 15 '16 at 13:18
  • Thanks to identify that. It will be great if I can do this in a single statement or property. I was just doing it for a test purpose and see how it works. – DOTNET Rocker Jun 15 '16 at 13:25

1 Answers1

0

Your properties bool Status and string Public are confusing and having 2 fields in the database representing the same thing is bad idea. You should have a single property (say)

public bool IsPublicPost { get; set; }

The checkbox alone should be sufficient to represent the value, but if you did want the extra column, then in the view model add an additional property (say)

public string PublicPostDisplayText
{
    get { return IsPublicPost ? "Yes" : "No"; }
}

or better, create a DisplayTemplate for the proeprty that outputs "Yes" or "No".

Next, you cannot correctly bind to a collection model using a foreach loop (refer HTML Table to ADO.NET DataTable for more details) and you need to use a for loop or custom EditorTemplate for typeof Product

for(int i = 0; i < Model.Items.Count; i++)
{
    <tr>
        <td>@Html.DisplayFor(m => m.Items[i].Id)</td> // see notes below
        ....
        <td>@Html.CheckBoxFor(m => m.Items[i].IsPublicPost, new { @class = "post-checkbox", data_id = Model.Items[i].Id })</td>
        // If you really want the extra column
        <td class="post-text">@Html.DisplayFor(m => m.Items[i].PublicPostDisplayText)</td>
    </tr>
}

Then the script to update the database will be

var url = '@Url.Action("UpdatePublicPostStatus", "Home")';
$('.post-checkbox').click(function() {
    var isChecked = $(this).is(':checked');
    var id = $(this).data('id');
    var row = $(this).closest('tr'); // only if you display the extra column
    $.post(url, { id: id, isPublicPost: isChecked }, function(response) {
        if (response) {
            // no need to do anything, but if you display the extra column
            var displayText = isChecked ? 'Yes' : 'No';
            row.find('.post-text').text(displayText);
        } else {
            // Oops something went wrong - reset the checkbox and display error?
        }
    }).fail(function() {
        // Oops something went wrong - reset the checkbox and display error?
    });
});

and finally the controller method

[HttpPost]
public JsonResult UpdatePublicPostStatus(int id, bool isPublicPost)
{
    MainDbContext db = new MainDbContext();
    var product = db.Product.Find(id);
    if (product != null) // always check
    {
        product.IsPublicPost = isPublicPost;
        db.Entry(product).State = EntityState.Modified;
        db.SaveChanges();
        return Json(true);
    }
    // If something went wrong, e.g. an exception, then return Json(null);
}

Side notes.

  1. Duplicate id attributes are invalid html (do not add then inside loops unless they are unique)
  2. Use style sheets rather that inline styles, e.g. td:nth-child(1) { text-align:center; }
  3. Using if (ModelState.IsValid) is pointless (and will always return true) in your case because your not posting and binding to a model.
Community
  • 1
  • 1
  • Thanks for the solution. But it is not updating. Do I need to include any other specific script to get it work? I've done the same as you provided. – DOTNET Rocker Jun 16 '16 at 12:03
  • Have you included `jquery-{version}.js`? What if any errors do you get in the browser console. Are you hitting the controller method. You need to give more details. –  Jun 16 '16 at 12:07
  • There are no errors and I've included the jQuery file, version - 1.10.2. But it does not update the database. – DOTNET Rocker Jun 16 '16 at 12:13
  • Is it hitting the controller method? (and I just noticed a typo - it should be `db.Entry(product).State = EntityState.Modified;` –  Jun 16 '16 at 12:17
  • Thanks again. I noticed that earlier and changed it according to mine. – DOTNET Rocker Jun 16 '16 at 12:21
  • I've tried now. I think, it is not hitting the controller. – DOTNET Rocker Jun 16 '16 at 12:36
  • it is not hitting the controller. I've put a breakpoint on the method. – DOTNET Rocker Jun 16 '16 at 13:26
  • Then your getting errors in the browser console. What are they? You need to learn the basics of debugging your code :) –  Jun 16 '16 at 13:28
  • I have seen the browser's console and found this: use-of-getpreventdefault-is-deprecated-use-defaultprevented-instead. I think and hope, this is not a big issue regarding the update. But still no luck to make it work. – DOTNET Rocker Jun 16 '16 at 19:14
  • Thanks a lot. I was able to make it work. Just took another controller and implemented perfectly. One more thing: I changed this var url = '@Url.Action("UpdatePublicPostStatus", "Home")' to var url = '@Url.Action("UpdatePublicPostStatus")'. Thanks again for guiding. – DOTNET Rocker Jun 18 '16 at 09:11