1

I have a form which I need to submit via ajax is submitting but not binding to the model.

My jquery source code is as below

function SubmitForm() {
console.log($("#PackageSubscription").serialize());
$.ajax({
    url: '/HelpDesk/CalculateCost',
    cache: false,
    processData: false,
    data: $("#PackageSubscription").serialize(),
    type: 'POST',
    success: function (data) {
    }
});

}

Exactly the same form does bind when I use the standard mvc submit (synchronous ) via submit button.

Interestingly the console log has all the form values and they are exactly the same when we submit the form synchronously ( Console log string from ajax call and form values from fiddler request inspector are exactly the same)

some example data is like

MemberId=12345678
&SitePackges[0].SiteId=50
&SitePackges[0].SizeIndicator=2
&SitePackges[0].LstPackageDisplayItems[0].PackageId=4
&SitePackges[0].LstPackageDisplayItems[0].Name=PAT and Emergency Lighting 
&SitePackges[0].LstPackageDisplayItems[0].IsSubscribed=True
&SitePackges[0].LstPackageDisplayItems[0].LastServiceDate=
&SitePackges[0].LstPackageDisplayItems[0].ProposedDate=
&SitePackges[0].LstPackageDisplayItems[1].PackageId=6
&SitePackges[0].LstPackageDisplayItems[1].Name=Fire Alarm and Extinguishers
&SitePackges[0].LstPackageDisplayItems[1].IsSubscribed=True
&SitePackges[0].LstPackageDisplayItems[1].LastServiceDate=
&SitePackges[0].LstPackageDisplayItems[1].ProposedDate=

and my viewmodel looks like

public class PpmTypePackageSubscriptionModel
{        
    public int MemberId { get; set; }
    public DateTime TimeStamp { get; set; }
    public List<SitePackage> SitePackges { get; set; }
    public double TotalCost { get; set; }

    public PpmTypePackageSubscriptionModel()
    {
        SitePackges=new List<SitePackage>();
    }
}

public class SitePackage
{
    public int SiteId { get; set; }
    public int SizeIndicator { get; set; } 
    public string Site { get; set; }

    public List<PackageDisplayItem> LstPackageDisplayItems { get; set; }

    public SitePackage()
    {
        LstPackageDisplayItems=new List<PackageDisplayItem>();

    }

}

See below the mark up of View

@using Booker.WebUI.Models
@model PpmTypePackageSubscriptionModel
@using (@Html.BeginForm("CalculateCost", "HelpDesk", FormMethod.Post, new { @class = "form", id = "PackageSubscription", name = "PackageSubscription" }))
{
@Html.AntiForgeryToken()
@Html.HiddenFor(x => x.MemberId)
<table class="table table-responsive table-condensed table-hover table-striped">
    @for (int i = 0; i < Model.SitePackges.Count; i++)
    {
        <tr>
            <td class="siteHeading">
                @Html.HiddenFor(x => x.SitePackges[i].SiteId) @Html.DisplayFor(x => x.SitePackges[i].Site)
                @Html.HiddenFor(x => x.SitePackges[i].SizeIndicator) @Html.DisplayFor(x => x.SitePackges[i].SizeIndicator)
            </td>
        </tr>
        <tr>
            <th class="col-sm-3">
                Name
            </th>
            <th class="col-sm-2">
                Subscribe
            </th>
            <th class="col-sm-2">
                Last Service Date If Known
            </th>
            <th class="col-sm-2">
                Proposed Date
            </th>
        </tr>

        for (int j = 0; j < Model.SitePackges[i].LstPackageDisplayItems.Count; j++)
        {
            <tr>
                <td>
                    @Html.HiddenFor(x => x.SitePackges[i].LstPackageDisplayItems[j].PackageId)
                    @Html.HiddenFor(x => x.SitePackges[i].LstPackageDisplayItems[j].Name)
                    @Html.DisplayFor(x => x.SitePackges[i].LstPackageDisplayItems[j].Name)
                </td>
                <td>
                    @Html.RadioButtonFor(x => x.SitePackges[i].LstPackageDisplayItems[j].IsSubscribed, true, new { @class = "pull-left " })<label class="pull-left">Yes&nbsp;&nbsp;</label>
                    @Html.RadioButtonFor(x => x.SitePackges[i].LstPackageDisplayItems[j].IsSubscribed, false, new { @class = "pull-left" })<label class="pull-left">No</label>
                </td>
                <td>
                    @Html.TextBoxFor(x => x.SitePackges[i].LstPackageDisplayItems[j].LastServiceDate, "{0:d MMM yyyy}", new { @class = "jquery_datepicker form-control", autocomplete = "off" })
                </td>
                <td>
                    @Html.TextBoxFor(x => x.SitePackges[i].LstPackageDisplayItems[j].ProposedDate, "{0:d MMM yyyy}", new { @class = "jquery_datepicker form-control", autocomplete = "off" })
                </td>
            </tr>
        }

    }
</table>

<div class="row">
  <div class="col-sm-3"><p>Parking Availability</p></div>
    <div class="col-sm-5">
        <select id="ParkingAvailability" class="form-control" name="ParkingAvailability">
            <option value="1">Free Carpark</option>
            <option value="2">Paid Carpark</option>
        </select>
    </div>
</div>
<div class="row">
    <div class="col-sm-3"><p>Access Time</p></div><div class="col-sm-5"> <input class="form-control" name="SiteContactEmail" value="0900-1500 MonSat">
</div
 </div>
<div class="row">
    <div class="col-sm-3">
        <input class="btn btn-primary" @*type="submit"*@ onclick="SubmitForm(); return false;" value="Calculate Cost" />
    </div>
    <div class="col-sm-5">
        <span id="totalCost" class="hide form-control"></span>
    </div>
</div>

<div class="row">
    <div class="col-sm-5 col-sm-offset-3">
        <input class="btn btn-success" type="submit" value="Submit For Approval" />
        <input class="btn btn-danger" type="submit" value="Cancel Signup" />
    </div>
</div>

}

My main view has the following mark up

<div id="tabs">
        <ul>
            <li><a href="#tabs-1">Member Sites </a></li>
            <li><a href="#tabs-2">Services and Packages</a></li>

        </ul>
        <div id="tabs-1">
            @Html.Partial("_MemberSites")
        </div>
        <div id="tabs-2">
            @Html.Action("GetAllPpmTypePackages", new { @id = @Model.MemberNumber })
        </div>

</div>

And my action methods looks like this

    [HttpGet]
    public ActionResult GetAllPpmTypePackages(int id)
    {
      // Processing to create the viewmodel and pass to view
    }
    [HttpPost]
    [ValidateAntiForgeryToken]
    public ActionResult CalculateCost(PpmTypePackageSubscriptionModel ppmTypePackageSubscriptionModel)
    {
    // Processing
    }
rumi
  • 3,293
  • 12
  • 68
  • 109
  • try removing `SitePackages` initialization from the constructor. – Amila Mar 12 '15 at 15:07
  • Sorry it does not make any difference – rumi Mar 12 '15 at 15:09
  • Try decorating the parameter in your controller action with `[FromBody]` like `public ActionResult ActionName([FromBody]PpmTypePackageSubscriptionModel value) – Amila Mar 12 '15 at 15:20
  • Still the same unfortunately – rumi Mar 12 '15 at 15:46
  • Try removing processData:false, by default it is true. Only when the raw data is to be sent, it is set to false – ssug89 Mar 12 '15 at 15:58
  • I have tried that already and it does not make a difference either – rumi Mar 12 '15 at 16:00
  • Have tested this and it works fine (as does using `$.post('@Url.Action("CalculateCost", "HelpDesk")', $('form').serialize(), function (data) { ...});` (although you do not need `processData: false`). But from your [last question](http://stackoverflow.com/questions/28949050/list-of-objects-not-binding-to-the-model-on-post-back-in-asp-net-mvc-4) you seemed to have changed the class from `SubscriptionModel` to `PpmTypePackageSubscriptionModel`. Are you sure you posting back the correct model to the correct action method? –  Mar 12 '15 at 22:25
  • 1
    @StephenMuecke thanks. I expect it to work fine too but failing to understand why its failing. Yes I have renamed the model and its not an issue. Could it be data related issue? I mean, may be mvc model binder does not ignore certain characters or data when the request comes via ajax. I have added `processData: false` just because of that reason and I will remove it and I m sure the problem existed even before I added that property. – rumi Mar 13 '15 at 06:59
  • 2
    Should not make any difference (you have even confirmed it yourself using fiddler). If you comment out the script and do a normal submit and it works, then so should the ajax submit. –  Mar 13 '15 at 07:07
  • Where and how are you calling this `SubmitForm` javascript function? What are you seeing in the Network tab of your web browser developer toolbar? Can you see the AJAX POST request being made? Your code looks fine and I have verified that it works. So it seems that the problem is in some other part of your code not shown here. Maybe if you could provide a short and concise example allowing to reproduce the issue we might be able to help further. – Darin Dimitrov Mar 15 '15 at 09:15
  • @DarinDimitrov yes I can see the request made in the network tab and in the fiddler i can see all the data passed with correct ids. I have added above other parts of page which might be creating the issue but I can't identify it unfortunately. I tried to reproduce the issue with the test app and test data and you are right it did work so the issue is definitely lying with some other part of the code. – rumi Mar 15 '15 at 09:40
  • This morning just changed the jquery ajax request from `POST` to `GET` and its all working as expected i.e. `$("#PackageSubscription").serialize()` is binding to the model but don't know why. – rumi Mar 16 '15 at 09:20
  • so it raises a new question why would a request work with ajax type GET and won't work with type POST? On the same page (different partial views) other POST requests work absolutely fine. Could it be some data? if so how can we skip such data – rumi Mar 16 '15 at 12:16
  • Why don't you use Ajax.BeginForm instead of Html.BeginForm? As you won't need any extra code for it and it will definitely work. – sandip patil Mar 20 '15 at 08:59

3 Answers3

0

If I understand correctly, the form doesn't update because you aren't doing anything with the data received from the ajax response.

You should bind the result in the ajax success function. Ajax is asynchronous, so the data is posted, but the response in not necessarily immediate. When a response is received successfully (no timeout, or connection loss, etc.) the ajax call goes to the success function, and the response data is stored, in your case, in the variable "data".

Try doing the following:

function SubmitForm() {
console.log($("#PackageSubscription").serialize());
$.ajax({
    url: '/HelpDesk/CalculateCost',
    cache: false,
    processData: false,
    data: $("#PackageSubscription").serialize(),
    type: 'POST',
    success: function (data) {
        alert(data); // This will only show the response for you, use for debugging only
        $("#MemberId").value(data.MemberId);
    }
});
Ricium
  • 29
  • 5
0

Remove processData:false from your ajax. Then it will surely bind with your model. Please read this below:

processData (default: true) Type: Boolean By default, data passed in to the data option as an object (technically, anything other than a string) will be processed and transformed into a query string, fitting to the default content-type "application/x-www-form-urlencoded". If you want to send a DOMDocument, or other non-processed data, set this option to false.

Rohit Arora
  • 2,246
  • 2
  • 24
  • 40
0

That might be a problem of serialize() function.

Try to use JSON.stringify instead.

<script type="text/javascript">
function OnBtnSubmitClick(s, e) {
    var item1 = SecurityCode.GetText();
    var item2 = SecurityCoderMnem.GetText();
    var item3 = SecurityRegNum.GetText();
    var item4 = SecurityIsin.GetText();
    var item5 = document.getElementById("DocOrderSecurityOut_Id").value;
    var item6 = document.getElementById("IsEdit").value;

    var jsmodel = {
        DocOrderSecurityOut: {
            SecurityCode: item1,
            SecurityCoderMnem: item2,
            SecurityRegNum: item3,
            SecurityIsin: item4,
            Id: item5
        },
        IsEdit: item6
    };

    $.ajax({
        url: '@Url.Action("AjaxForm", "DocOrderSecurityOut")',
        data: JSON.stringify(jsmodel),
        dataType: 'html',
        contentType: "application/json; charset=utf-8",
        type: "POST",
        beforeSend: function () { loadingPanel.Show(); },
        complete: function () { loadingPanel.Hide(); },
        error: function (x, status, error) {
            alert("Error code: " + x.status + '\n' +
                "State: " + status + ". More info: " + error);
        },
        success: function (response) {
            $("#container").html(response);
        }
    });
}

Controller:

[HttpPost]
    public ActionResult AjaxForm(DocOrderSecurityOutViewModel model)
    {
        // magic
    }

In Global.asax:

        ValueProviderFactories.Factories.Add(
            new JsonValueProviderFactory());
Ice2burn
  • 677
  • 1
  • 8
  • 19