LP13
LP13

Reputation: 34149

Use jQuery to post data to MVC action method

I'm trying to post data using jQuery Ajax to MVC action using the approach below. But inside the controller all model properties are always null. Not sure what I'm missing here.

.CSHTML

<form id="MyForm">
<input name="PersonName" type="text" />
<input name="Address" type="text" />
<select name="States" multiple="multiple">
    <option value="TX">Texas</option>
    <option value="OK">Oklahoma</option>
    <option value="OH">Ohio</option>
</select>
<select name="Status">
    <option value="1">Active</option>
    <option value="2">Deleted</option>
    <option value="3">Pending</option>
</select>
<input type="button" value="Save" id="Save" />

JavaScript

$(function () {
$("#Save").click(function (e) {
    var dataToPost = $("#MyForm").serialize()
    $.ajax(
    {
        type: "POST",
        data: JSON.stringify(dataToPost),
        url: "Working/Save",
        contentType: 'application/json; charset=utf-8'
    })
  })
})

Controller

public class WorkingController : Controller
{
    // GET: Working
    public ActionResult Index()
    {
        return View();
    }

    public ActionResult Save(WorkingModel model)
    {
        // All model properties are null here????

        return Json("Success");
    }
 }

Model

public class WorkingModel
{
    public string PersonName { get; set; }
    public string Address { get; set; }
    public string[] States { get; set; }
    public string Status { get; set; }
}

EDIT1
I have added the model above. Here the serialized data and JSON stringify data when I click on save.

Serialized data

"PersonName=Foo&Address=123+Test+Drive&States=TX&Status=1"

After JSON.Stringify

"\"PersonName=Foo&Address=123+Test+Drive&States=TX&Status=1\""

I have tried adding HttpPost attribute and [FromBody] attribute with no luck.

I don't think I have to change the return type from ActionResult to JsonResult.

Also the URL is correct because the debugger is hitting inside the action method where I can QuickWatch the model properties.

Note that it works if I create JSON object and post it like below:

var dataToPost = {
    PersonName:'Foo',
    Address: '123 Test Drive',
    State: 'TX',
    Status: 1
 }

Upvotes: 10

Views: 69334

Answers (4)

Racil Hilan
Racil Hilan

Reputation: 25361

I think that you should submit standard HTML form data rather than JSON data. Hence change the lines:

data: JSON.stringify(dataToPost),
contentType: 'application/json; charset=utf-8'

to

data: dataToPost,
contentType: 'application/x-www-form-urlencoded; charset=UTF-8'

Note: You can also omit the second line because that is the default contentType for $.ajax According to jQuery documentation.

EDIT 1 This is in response to your comment and edit.

What I was trying to tell you is that the data that your Ajax is sending must match how you're processing the data received by your action. The reason that your model properties are null is that those two don't match.

You did not post the code for your action, so we have no idea how you're processing the data, but now from your edit 1, it seems you're processing the data received as JSON data, because it worked when you sent it real JSON data.

So you have to do one of two things:

1- Send real JSON data: Simply using JSON.stringify does not mean your data is now appropriate JSON data. As you found, JSON.stringify simply wrapped your string with quotation marks making it a valid JSON string, that's all. But that's not what your action is expecting, it is expecting a JSON object. To send a JSON object, you probably need to write a function that takes the form fields one by one and build a JSON object, then call this function instead of JSON.stringify.

2- Send standard form data: That's what I was suggesting in my answer above. To make it work, simply delete all the code in your action that is processing it as a JSON object. MVC is designed to process standard form data by default, so you don't need any additional processing. Simply send it standard form data and it will work.

Note: Ajax does not need to be sending/receiving data in JSON format. JSON format is very useful for exchanging data in many scenarios, but you choose the format that suits your specific scenario. For example, your action is sending back a JSON object just for the result: return Json("Success"). If you only want to send simple result (success vs. failure), you can return a simple string (e.g. return "Success") or even a boolean/integer (e.g. return "True" or return "1"). Jason objects need extra processing to parse them from the string. While this processing is very fast and efficient, it is still a bit faster to parse and process simple data types like string, boolean, or integer if you don't need to send additional info.

Upvotes: 7

Tieson T.
Tieson T.

Reputation: 21239

Your JavaScript/jQuery code can be significantly simplified, which might be the best route to go:

$(function () {
    $("#MyForm").on('submit', function (e) {

        e.preventDefault() // prevent the form's normal submission

        var dataToPost = $(this).serialize()

        $.post("Working/Save", dataToPost)
            .done(function(response, status, jqxhr){ 
                // this is the "success" callback
            })
            .fail(function(jqxhr, status, error){ 
                // this is the ""error"" callback
            })
    })
})

You should handle the onsubmit event of the form, rather than the onclick event of the button - it's possible for something other than the button to cause the form to be submitted. In this case, we want to prevent the form's default submit behavior, since we're submitting the form with AJAX.

.serialize() already handles encoding the form correctly, so you don't need to JSON-encode the form values. Doing so is most likely the reason that the modelbinder isn't rebuilding the model when processing the request.

$.post is a helper function that wraps the common setup work you need for $.ajax - the version shown here wants the URL to POST to, and the data to POST. If your jQuery code is in a script element within a View, then you probably want to use the Url.Action() helper - it will build the correct URL based on your routing rules. If you elect to go that route, you would use something similar to:

$.post('@Url.Action("Save", "Working")', dataToPost)

Then, we handle the successful response (anything with a HTTP-200 status code) and the failed response (anything else, basically) using the relevant helpers. What you do in those helpers is up to you.

Upvotes: 14

LP13
LP13

Reputation: 34149

i just have to remove the content type from post and dont stringify it and it worked

$(function () {
  $("#Save").click(function (e) {
  var dataToPost = $("#MyForm").serialize()
  $.ajax(
  {
    type: "POST",
    data: dataToPost,
    url: "Working/Save"
  })
 })
})

Upvotes: 5

Michael Crook
Michael Crook

Reputation: 1535

Move previous answer text to pastebin as I was wrong, answer is as follows:

Just read your edit, your problem is JSON thing looks funny: "\"PersonName=Foo&Address=123+Test+Drive&States=TX&Status=1\"" That will not translate into WorkingModel.

My recomendation is to create a custom JS object and post it. I just fired up a VS MVC project and made it, it all is working :)

$(function() {
    $("#Save").click(function(e) {
        var personName = $("[name='PersonName']").val();
        var address = $("[name='Address']").val();
        var states = $("[name='States']").val();
        var status = $("[name='Status']").val();
        var dataToPost = {
            PersonName: personName,
            Address: address,
            States: states,
            Status: status
        };

        $.ajax(
        {
            type: "POST",
            data: JSON.stringify(dataToPost),
            url: "Save",
            contentType: 'application/json; charset=utf-8'
        });
    });
});

Hope this helps!

Upvotes: 1

Related Questions