Use jQuery to post data to MVC action method

66,158

Solution 1

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.

Solution 2

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.

Solution 3

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"
  })
 })
})

Solution 4

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!

Share:
66,158
LP13
Author by

LP13

Updated on July 22, 2022

Comments

  • LP13
    LP13 almost 2 years

    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
     }
    
  • Admin
    Admin over 8 years
    You do not need to specify [HttpPost] (the ajax options include type: "POST",). You do not need to change ActionResult to JsonResult (JsonResult is ActionResult). You do not need [FromBody] (that relates to webapi)
  • Michael Crook
    Michael Crook over 8 years
    Why not specify [HttpPost] I'm pretty sure (not completely) that save isn't a method name that MVC can automatically convert. JsonResult isn't needed, but it makes more sense as it is returning a JsonResult. But maybe your correct as he is hitting the method, it's just nothings coming along with it. Try looking into what the JS creats with JSON.stringify maybe that's not working and your passing null up
  • Admin
    Admin over 8 years
    You can (and really should) include the [HttpPost]attribute, but its not required in this case because of the ajax call. Ditto for JsonResult (you can, and probably should, but its not necessary). OP's issues are not related to anything you have in your answer. Could be the url is incorrect (it should be /Working/Save") or the model has only fields (no get; set;) but who knows until more information is provided. And in any case, OP should remove contentType: and not stringify the data
  • Admin
    Admin over 8 years
    OP is submitting JSON. That's what data: JSON.stringify(dataToPost), does!
  • Racil Hilan
    Racil Hilan over 8 years
    @StephenMuecke Thank you for your notice. Of course, you're right about what that line does, but I meant to say that he shouldn't be submitting Json. I should've included that line in my answer. I will update my answer.
  • Michael Crook
    Michael Crook over 8 years
    Ok, it should work for you now :) just watch your browsers console as I think your routing might differ to my setup, I had to chagne the url of the post in my solution
  • LP13
    LP13 over 8 years
    @RacilHilan thanks for your reply. Model properties does match with the name of the html control and they are string which is nullable. Also how does it matter how the action method is processing model inside after it receives model, because for action method it will be simple C# object?
  • Racil Hilan
    Racil Hilan over 8 years
    Hmm, you have a good point about the processing afterwards. Like I said, we don't see your action code so it's all guessing there. But apart from that, my answer still stands, so have you tried the solution I gave above?
  • LP13
    LP13 over 8 years
    @MichaelCrook thanks! That is how we are doing right now by creating a json object manually and posting it and its working. However now consider if we have to post large object it will be time consuming process. that why i would to seralize the form and post it. Also note that type of button is not "submit"
  • LP13
    LP13 over 8 years
    Also note that type of the input is not "Submit"
  • Racil Hilan
    Racil Hilan over 8 years
    Yes of course, because you're submitting it using Ajax. My suggestion was that you Ajax submission should mimic a standard form submission.
  • Michael Crook
    Michael Crook over 8 years
    The reason why is because $.serialize doesn't convert to JSON, it converts it to somthing you can post in a URI (www.yoursite.com/post?PersonName=a&Address=b&States=OK&Stat‌​es=OH&Status=3 see this: jsfiddle.net/n7rpzr6x If you want to convert a form automagically into JSON have a look into this stackoverflow question, the answer is awesome (with over 1000 upvotes) stackoverflow.com/questions/1184624/…
  • Michael Crook
    Michael Crook over 8 years
    HA! Very smart, didn't even consider just submitting as form data. FYI, this solution sends data to the server in this format: PersonName=a&Address=b&States=TX&States=OK&Status=2 Rather than JSON. But that is perfectly fine :).. You will find though that as your forms get more complex, manually constructing a JS Object (like in my answer) will work better as automagic stuff like $.serialize doesn't customize very well.
  • Michael Crook
    Michael Crook over 8 years
    Oh and you can also post it as a part of the body as form data, like the answer above does
  • LP13
    LP13 over 8 years
    @MichaelCrook thanks. Now your answer started me thinking again :) So far we were submitting small amount of data (5-6 fields) and thats why we were constructing json object by hand. For complex page (30-40 inputs + grids) i didn't want to construct json object by hand. i thought Form serialization will do the trick. Do we know any specific issues with $.serialize method?
  • Michael Crook
    Michael Crook over 8 years
    just that if your way of displaying the data to the user doesn't map strait from $.serialize to your ASP.NET model you will be forced to map it to an intermediary model like I did. If your starting to get big forms like that though, may I recoemend that you learn Knockout.js, its a data binding framework for web. That way your HTML is bound to your JS objects, then you just JSON.stringify your js models and it will automatically have all your form data already ready to rock!
  • Admin
    Admin over 8 years
    @MichaelCrook, That's nonsense. Using .serialize() will always work correctly if the view is constructed using the HtmlHelpers based on a model.
  • Admin
    Admin over 8 years
    @user3862378, Since Tieson T. provided the correct answer previously, that's the one you should have accepted, or at the very least up-voted it