sharlene sharlene - 1 month ago 8
Ajax Question

Parse checkbox change value to Model and then to JSON object on button click in VIEW ASP.Net MVC

Working with a form where user should be able to uncheck pre selected

checkbox
and save that state on
AJAX
post through
JSON
object to the
Controller
in
ASP.Net MVC


enter image description here

Requirement:


  1. Unchecking a pre selected checkbox should change a row in the database property from "Active" = 'Y' to "Active=" = 'N'

  2. Newly selected checkbox would create a complete new row in the db



Problem:


  1. After unchecking the selected text box Model is not updated in
    VIEW
    . Hence serverside data is not updated. Requires a client side update before data is parsed into
    AJAX
    call.

  2. Don't know how to capture the checkbox value change and pass it to the Model so that parsed data can then be changed to button click/submit form.

  3. Following code also gives error 200 because dataType is supposedly incorrect on submit (doing a button submit here).



View:

@model List<DashboardPermissionTool.ViewModels.UserViewModel>
@using (@Html.BeginForm(new { id = "UserForm" }))
{
<table class="table">
<tr>
<th>User</th>
@for (int i = 0; i < Model[0].Roles.Count; i++)
{
<th>
@Model[0].Roles[i].RoleName
</th>
}
</tr>
@for (int i = 0; i < Model.Count; i++)
{
<tr>
<td>
@Html.HiddenFor(m => m[i].UserName)
@Model[i].UserName
</td>
@for (int j = 0; j < Model[i].Roles.Count; j++)
{
<td>
@Html.CheckBoxFor(m => m[i].Roles[j].IsSelected)
</td>
}
</tr>
}
</table>

<div class="form-actions">
<a id="SubmitUserRoles" class="btn btn-success submit" value="Save">Save changes</a>
</div>

<script>

var parsedData = @Html.Raw( new System.Web.Script.Serialization.JavaScriptSerializer().Serialize(Model));

$(document).ready(function() {
$("#SubmitUserRoles").click(function () {

$('input[type=checkbox]').on("change",function(){
if($(this).prop('checked'))
{
$(this).attr('checked', false);
}
else
{
$(this).attr('checked', true);
}
});

$.ajax({
url: '@Url.Action("Create", "User")',
type: 'POST',
contentType: 'application/json; charset=utf-8',
cache: false,
dataType: 'json',
data: JSON.stringify(parsedData),
success: function (data) {
console.log(data);
}, error: function (xhr, ajaxOptions, error) {
alert(xhr.status);
alert("Error: " + xhr.responseText);
}
});
});
});
</script>


}

HTML Response: : For each selected check box Response is as follows:

<td>
<input checked="checked" data-val="true" data-val-required="The IsSelected field is required." name="[0].Roles[0].IsSelected" type="checkbox" value="true" />
<input name="[0].Roles[0].IsSelected" type="hidden" value="false" />
</td>


For each unchecked check box Response is as follows:

<td>
<input data-val="true" data-val-required="The IsSelected field is required." name="[0].Roles[1].IsSelected" type="checkbox" value="true" />
<input name="[0].Roles[1].IsSelected" type="hidden" value="false" />
</td>


ViewModels:

public class UserViewModel
{
public string UserName { get; set; }
public List<RoleViewModel> Roles { get; set; }
}

public class RoleViewModel
{
public string RoleName { get; set; }
public bool IsSelected { get; set; }
}


Controller:

[HttpPost]
public ActionResult Create(List<UserViewModel> model)
{

for (int i = 0; i < model.Count; i++)
{
for (int j = 0; j < model[i].Roles.Count; j++)
{
db.User.Add(new User
{
username = model[i].UserName,
role = model[i].Roles[j].RoleName
});
}

db.SaveChanges();
return RedirectToAction("Index", "User");
}
return View("Index", model);
}

Answer

There are multiple issues with your code.

Starting with the controller method, you have a return statement inside the outer for loop which means that you only ever save the values of the 1st UserViewModel and then exit the method. To save all records, it needs to be

[HttpPost]
public ActionResult Create(List<UserViewModel> model)
{
    for (int i = 0; i < model.Count; i++)
    {
        for (int j = 0; j < model[i].Roles.Count; j++)
        {
            db.User.Add(new User { username = model[i].UserName, role = model[i].Roles[j].RoleName });                  
        }
    }
    db.SaveChanges();
    return RedirectToAction("Index", "User");
}

But its clear from this method, that you want to redirect to the Index() method after saving the records, so using ajax to submit the data is pointless (ajax calls stay on the same page and do not redirect) so you should be using a normal submit button to submit the form (and delete all your scripts). You need to change

<a id="SubmitUserRoles" class="btn btn-success submit" value="Save">Save changes</a>

to

<input type="submit" class="btn btn-success submit" value="Save" />

and note also that value is not a valid attribute for an <a> element.

But to address the other issues with your code, lets assume you do want to stay on the same page and update the DOM.

First

var parsedData = @Html.Raw( new System.Web.Script.Serialization.JavaScriptSerializer().Serialize(Model));

(and your can simply use var parsedData = @Html.Raw(Json.Encode(Model)) instead) is serializing the original model, so you just posting back the original values, not the edited values. In order to pass the edited values, you ajax option needs to be

data: $('form').serialize(),

Next

contentType: 'application/json; charset=utf-8',

means you send the data as json, but you have not stringified the data. It would need to be

data: JSON.stringify($('form').serialize()),

in order to bind, but that is unnecessary and you would just need to remove the contentType option so that it uses the default 'application/x-www-form-urlencoded; charset=UTF-8'

Next

dataType: 'json',

means that your specifying that the method return json, but in fact it is returning html so it would throw an exception. It would need to be dataType: 'html', or simply remove the option and the ajax method will work it out.

You also need to delete your $("#SubmitUserRoles").click(function () { ... } script. The presence of the checked attribute means its selected and checked="checked" or checked="true" or check="false" or checked="anything" all mean exactly the same thing - that the checkbox is selected. Fortunately that code is never executed (if it did, it would screw everything up) because you only add the handler inside the handler for $("#SubmitUserRoles").click(function () { which throws an exception.

Finally

@using (@Html.BeginForm(new { id = "UserForm" }))

is adding a route value for id (if you look at the html your generating, you will see action="../Create/UserForm" (oraction="../Create?id=UserForm" depending on your routes (and your method does not have a parameter string id). I assume you think its adding an id attribute to the <form> tag, in which case you need to use one of the overloads of BeginForm() that accept htmlAttributes. However, its only necessary to add an id attribute for use with css or javascript selectors, so you can just use

@using (@Html.BeginForm())