Reputation: 21
I have an asynchonous httpPost that serializes my form data and commits it to my controller. There I try to get my form data serialized into my view model class but all the values are null or have the default value assigned.
public ActionResult GetSalesData(string vmString)
{
-serialization...
-use the data to select some other data
return new JsonResult { Data = d, JsonRequestBehavior = JsonRequestBehavior.AllowGet };
}
For the serialization I tried so far these two ways:
1.)
{ vmString: $('form').serialize() }
result:
Dataset.Ids=3,7,12&Type=Sales&DataSources=BeverageType&DisplayModes=Volume&SeriesTypes=Lines&SalesVm.DisplayOptions.ShowAverage=false&SalesVm.DisplayOptions.ShowTargetLine=false&ActionName=Sales_SelectBeverageTypes&Mode=LightboxInWizard&SelectedDatasetIdsWorkingCopy=3&SearchTerm=
2.)
{ vmString: JSON.stringify($('form')) }
result:
{"length":2,"prevObject":{"0":{"jQuery1710039964356962994385":1,"location":{}},"context":{"jQuery1710039964356962994385":1,"location":{}},"length":1},"context":{"jQuery1710039964356962994385":1,"location":{}},"selector":"form","0":{"Dataset.Ids":{},"Type":{},"DataSources":{"jQuery1710039964356962994385":15},"3":{"jQuery1710039964356962994385":16},"DisplayModes":{"0":{"jQuery1710039964356962994385":42},"1":{"jQuery1710039964356962994385":43},"2":{"jQuery1710039964356962994385":44},"3":{"jQuery1710039964356962994385":45}},"SeriesTypes":{"0":{"jQuery1710039964356962994385":46},"1":{"jQuery1710039964356962994385":47}},"SalesVm.DisplayOptions.ShowAverage":{"0":{"jQuery1710039964356962994385":48},"1":{"jQuery1710039964356962994385":49}},"SalesVm.DisplayOptions.ShowTargetLine":{"0":{"jQuery1710039964356962994385":50},"1":{"jQuery1710039964356962994385":51}}},"1":{"ActionName":{},"Mode":{},"SelectedDatasetIdsWorkingCopy":{},"SearchTerm":{"jQuery1710039964356962994385":35}}}
To deserialize, I tried:
m = (StatisticsViewerViewModel)new JsonSerializer().Deserialize(new System.IO.StringReader(vmString), typeof(StatisticsViewerViewModel));
m = (StatisticsViewerViewModel)new JsonSerializer().Deserialize(new System.IO.StringReader(vmString), vmString.GetType());
m = JsonConvert.DeserializeObject<StatisticsViewerViewModel>(vmString);
DataContractJsonSerializer ser = new DataContractJsonSerializer(typeof(StatisticsViewerViewModel));
m = (StatisticsViewerViewModel) ser.ReadObject(stream);
I checked that all my fields are inside the form:
@using (Html.BeginForm())
{
some partial views containing some fields, all using the same model..
}
I also checked that the used View Model as well as all nested models do have an empty constructor.
Are there different ways to de-/serialize the form data? And what else could I check to make sure everything is as it should be?
ViewModel-Root:
public class StatisticsViewerViewModel
{
public String BreadcrumbName { get; set; }
public String ActionName { get; set; }
public StatisticsType Type { get; set; }
public DropDownListModel DataSourceList { get; set; }
public MultiSelectionModel Dataset { get; set; }
public SalesViewModel SalesVm { get; set; }
//public EventsViewModel EventsVm { get; set; }
public ChartExportOptions ExportOptions { get; set; }
public StatisticsViewerViewModel()
{
DataSourceList = new DropDownListModel();
Dataset = new MultiSelectionModel();
SalesVm = new SalesViewModel();
ExportOptions = new ChartExportOptions();
}
public enum StatisticsType
{
Sales,
Events
}
}
Nested ViewModels
public class SalesViewModel
{
public SalesDisplayOptions DisplayOptions { get; set; }
public RadioButtonModel DisplayModes { get; set; } // Volume, Value, ..
public RadioButtonModel SeriesTypes { get; set; } // Line, Bar, ..
public SalesViewModel(bool initialize = false)
{
if (initialize) { Initialize(); }
}
}
public class SalesDisplayOptions
{
public DisplayMode Mode { get; set; }
public SeriesType Type { get; set; }
public bool ShowAverage { get; set; }
public bool ShowTargetLine { get; set; }
public enum SeriesType
{
Lines, ...
}
public enum DisplayMode
{
Value, ...
}
}
A relevant point might be that the post is fired by kendoChart. Here the interesting block inside the view:
....
<div id="chart"></div>
....
@this.ScriptBlock(
@<script type="text/javascript">
$("#chart").kendoChart({
dataSource: new kendo.data.DataSource({
transport: {
read: {
url: actionUrl,
data: { vmString: $('form').serialize() },
dataType: "json",
contentType: "application/json; charset=utf-8"
}
},
sort: ...
....
To check whether the problems are related to the kendoChart-Object, I testwisely implemented a manual POST:
....
<div class="round-corner-bottom-right">
<button id="send-form" type="button">
<span class="button-label">Send Form Data</span>
</button>
</div>
....
@this.ScriptBlock(
@<script type="text/javascript">
$('document').ready(function () {
$('#send-form').on('click', function () {
var data = $('form').serialize();
$.ajax({
type: "POST",
url: actionUrl,
data: data,
cache: false,
success: function (returnData) {
}
});
}); // document ready
The problem does not persist in this scenario, all values are set as expected. So there seems to be a problem with the kendoChart-methode.
Upvotes: 2
Views: 2628
Reputation: 21
the problem layed not in the de-/serialization precedure itself but in the way how the data is bound in the datasource of the kendoChart. the solution to the problem has been to specify a function with an ajax post as datasource like shown below:
var data = $('form').serialize();
$("#chart").kendoChart({
title: {
text: "Solution"
},
dataSource: new kendo.data.DataSource({
transport: {
read: function (options){
$.ajax({
url: actionUrl,
data: data,
type: "POST",
cache: false,
success: function (result) {
options.success(result);
}
});
}
},
....
Sorry for bothering you with the wrong questions!
Upvotes: 0
Reputation: 2706
In my experience, the only simple way to pass complex JS objects to a Asp.net Controller Action and get the data to serialize into a viewmodel, is to preformat the object on the JS end first.
This page explains the method: http://erraticdev.blogspot.com/2010/12/sending-complex-json-objects-to-aspnet.html
To preformat the data you would do something like this:
$.post(URL, $.toDictionary({ ... }), function(response) { ... });
Here's the minified source of the jQuery.toDictionary plugin:
/*!
* jQuery toDictionary() plugin
*
* Version 1.2 (11 Apr 2011)
*
* Copyright (c) 2011 Robert Koritnik
* Licensed under the terms of the MIT license
* http://www.opensource.org/licenses/mit-license.php
*/
(function (a) { if (a.isFunction(String.prototype.format) === false) { String.prototype.format = function () { var a = this; var b = arguments.length; while (b--) { a = a.replace(new RegExp("\\{" + b + "\\}", "gim"), arguments[b]) } return a } } if (a.isFunction(Date.prototype.toISOString) === false) { Date.prototype.toISOString = function () { var a = function (a, b) { a = a.toString(); for (var c = a.length; c < b; c++) { a = "0" + a } return a }; var b = this; return "{0}-{1}-{2}T{3}:{4}:{5}.{6}Z".format(b.getUTCFullYear(), a(b.getUTCMonth() + 1, 2), a(b.getUTCDate(), 2), a(b.getUTCHours(), 2), a(b.getUTCMinutes(), 2), a(b.getUTCSeconds(), 2), a(b.getUTCMilliseconds(), 3)) } } var b = function (c, d, e, f) { if (a.isPlainObject(c)) { for (var g in c) { if (f === true || typeof c[g] !== "undefined" && c[g] !== null) { b(c[g], d, e.length > 0 ? e + "." + g : g, f) } } } else { if (a.isArray(c)) { a.each(c, function (a, c) { b(c, d, "{0}[{1}]".format(e, a)) }); return } if (!a.isFunction(c)) { if (c instanceof Date) { d.push({ name: e, value: c.toISOString() }) } else { var h = typeof c; switch (h) { case "boolean": case "number": h = c; break; case "object": if (f !== true) { return }; default: h = c || "" } d.push({ name: e, value: h }) } } } }; a.extend({ toDictionary: function (c, d, e) { c = a.isFunction(c) ? c.call() : c; if (arguments.length === 2 && typeof d === "boolean") { e = d; d = "" } e = typeof e === "boolean" ? e : false; var f = []; b(c, f, d || "", e); return f } }) })(jQuery)
Upvotes: 0
Reputation: 3221
Have you tired using allowing default model binder to handle it rather than trying to deserialize and managing it yourself?
So
public ActionResult GetSalesData(string vmString)
becomes this
public ActionResult GetSalesData(MyModel vmString)
Upvotes: 1