1

I am having issues with the data submitted to my controller action via a knockout model. Each string field has extra double quotes surrounding it which is an issue.

While I can deserialize the data manually, it seems that this issue should not be occurring at all when using MVC3. Keep in mind that I need to do a form submit not an ajax post.

Html:

@inherits System.Web.Mvc.WebViewPage<FbWizardCreateTabModel>

@using (Html.BeginForm("InstallApplication", "FbWizard", FormMethod.Post, new { id = "createtab", @data_bind = "submit:onSubmit" }))
{
    <p>Page Id: <span data-bind="text: PageId"></span></p>
    <p>Page Name: <span data-bind="text: PageName"></span></p>
    <p>Tab Name: @Html.TextBoxFor(m => m.TabName, new { data_bind = "value: TabName" })</p>
}

<button class="btn btn-primary next">Submit</button>

Script code:

 <script type="text/javascript">

        var initialData = @Html.Raw(Model.JsonData);
        var viewModel = ko.mapping.fromJS(initialData);

        viewModel.onSubmit = function() {

            var action = $('#createtab').attr('action');

            ko.utils.postJson(action, this);

            return false;
        };

        ko.applyBindings(viewModel);

    </script>

Controller action:

[HttpPost]
public ActionResult InstallApplication(FbWizardCreateTabModel model)
{
    // The model is mangled at this point, see image below

    return View();
}

Contents of model after post:

enter image description here

Raw post data:

POST http://mysite.localhost:7785/Admin/FbWizard/InstallApplication HTTP/1.1
Host: mysite.localhost:7785
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64; rv:12.0) Gecko/20100101 Firefox/12.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-us,en;q=0.5
Accept-Encoding: gzip, deflate
Connection: keep-alive
Referer: http://mysite.localhost:7785/Admin/FbWizard/CreateTab
Cookie: fbsr_....
Content-Type: application/x-www-form-urlencoded
Content-Length: 333

PageId=%22231271443653720%22&PageName=%22Car5%22&TabName=%22Auctions2%22&JsonData=null&__ko_mapping__=%7B%22include%22%3A%5B%22_destroy%22%5D%2C%22ignore%22%3A%5B%5D%2C%22copy%22%3A%5B%5D%2C%22mappedProperties

What am I doing wrong?

rboarman
  • 8,248
  • 8
  • 57
  • 87
  • I'm just having a look at the source code for postJson, it seems to make all the inputs, but when it puts the value in, it calls ko.utils.stringifyJson, which seems to put quotes around things. Which I think is wrong in this case? so it might be a bug – Keith Nicholas May 23 '12 at 22:17

2 Answers2

1

I might be WAY off base here..... but POSSIBLY this is a bug.... what follows is the source code from knockout with a minor change

   postJsonNoQuotes = function (urlOrForm, data, options) {
        options = options || {};
        var params = options['params'] || {};
        var includeFields = options['includeFields'] || this.fieldsIncludedWithJsonPost;
        var url = urlOrForm;

        // If we were given a form, use its 'action' URL and pick out any requested field values
        if((typeof urlOrForm == 'object') && (ko.utils.tagNameLower(urlOrForm) === "form")) {
            var originalForm = urlOrForm;
            url = originalForm.action;
            for (var i = includeFields.length - 1; i >= 0; i--) {
                var fields = ko.utils.getFormFields(originalForm, includeFields[i]);
                for (var j = fields.length - 1; j >= 0; j--)
                    params[fields[j].name] = fields[j].value;
            }
        }

        data = ko.utils.unwrapObservable(data);
        var form = document.createElement("form");
        form.style.display = "none";
        form.action = url;
        form.method = "post";
        for (var key in data) {
            var input = document.createElement("input");
            input.name = key;
           // I think this is the offending line....
           // input.value = ko.utils.stringifyJson(ko.utils.unwrapObservable(data[key]));
            input.value = ko.utils.unwrapObservable(data[key]);
            form.appendChild(input);
        }
        for (var key in params) {
            var input = document.createElement("input");
            input.name = key;
            input.value = params[key];
            form.appendChild(input);
        }
        document.body.appendChild(form);
        options['submitter'] ? options['submitter'](form) : form.submit();
        setTimeout(function () { form.parentNode.removeChild(form); }, 0);
    }
}
Keith Nicholas
  • 43,549
  • 15
  • 93
  • 156
  • This line is throwing an error: input.value = ko.utils.unwrapObservable(data[key]).toString();, error: Error: Unable to get value of the property 'toString': object is null or undefined – rboarman May 23 '12 at 23:32
  • maybe just remove the toString altogether – Keith Nicholas May 23 '12 at 23:41
  • I'm just flying by the seat of my pants here as I don't have a code to play with to test. – Keith Nicholas May 23 '12 at 23:42
  • That worked. Do you have a connection to get the bug fixed for real? – rboarman May 24 '12 at 02:31
  • well on the forum, there is also a link to http://stackoverflow.com/questions/6793377/how-to-do-a-simple-form-post-to-mvc-3-application-and-obtain-deserialized-class/6797718#6797718 however, I still think the form url encoding is done incorrectly by postJson. – Keith Nicholas May 24 '12 at 03:06
  • That post looks like it applies to MVC2. The FromJson attribute has been deprecated in MVC3 as far as I know. Thank you for your help! – rboarman May 24 '12 at 15:45
  • I don't think it is a bug, if it is changed, apps that use the code could stop working. If you actually want to post json, you don't create form elements, you change the content type and send the json as a raw string, not url encoded values. – Jason Goemaat May 24 '12 at 18:38
  • So the questions remains, how do you submit a form and have model binding work properly? – rboarman May 25 '12 at 00:45
  • the answer is, you can't. Well not by the method you are using. You CAN submit JSON directly which is model bound. Which is what I do. But via form url encoding, the intention of postJson is apprently to post form url encoded json.... so as you found out, it posts strings of data and in your case, that data was single values, but you can post json object for each input. – Keith Nicholas May 25 '12 at 02:55
  • meaning you need to deserialize the form url encoded data from json before you can use it, which is what the other link was all about. – Keith Nicholas May 25 '12 at 02:55
0

The key is in the name of the function, postJson. It is not meant to do what you are trying to do with it, it takes any properties on the object you give it and posts their values as JSON. For instance you could change your post to this:

ko.utils.postJson(action, { json: this });

And your action to this:

[HttpPost]
public ActionResult InstallApplication(string json)
{
    JavascriptSerializer ser = new JavascriptSerializer();
    FbWizardCreateTabModel model = ser.Deserialize<FbWizardCreateTabModel>(json);

    return View();
}

If you want to use a function like Keith suggests, I would leave JSON out of the name because you do not seem to want to actually post JSON, maybe call it postAsFormData

Jason Goemaat
  • 28,692
  • 15
  • 86
  • 113
  • MVC3 handles Json binding just fine. Manually deserializing is not necessary or recommended any more. – rboarman May 24 '12 at 15:47
  • That's not what is happening, postJson is posting the json as string values, just as if you typed text that happened to be json into a text box. – Jason Goemaat May 24 '12 at 18:33