I have a problem with the datasource, when binding it to an asp.net MVC backend controlller for READ and UPDATE.
The READ function works when using the parameterMap like :
if (operation == "read") {
return options;
};
The UPDATE function, however, does NOT:
if (operation != "read" && options.models) {
return { models: kendo.stringify(options.models) };
};
When saving the grid back to the backend controller, an error occurs in kendo.all.js.
This i my Datagrid - sourcecode:
var gridDatasource = new kendo.data.DataSource({
transport: {
read: {
url: "@Url.Action("GetBudgetData","Dagsbudget")",
dataType: "json",
type: "POST",
data:
{
butik: $("#location").data("kendoDropDownList").value(),
budgetyear: periodeSelector.getFullYear(),
budgetmonth: periodeSelector.getMonth() + 1
}
},
update: {
url: "@Url.Action("PutBudgetData","Dagsbudget")",
type: "POST",
dataType: "json",
},
parameterMap: function (options, operation) {
if (operation != "read" && options.models) {
return { models: kendo.stringify(options.models) };
};
if (operation == "read") {
return options;
};
},
success: function (e) {
alert("Get budget data success");
},
error: function (xhr, status, error) {
debugger;
alert(error);
},
schema: {
model: {
fields: {
location: { type: "text", ediable: false },
date: { type: "date", editable: false, format: "dddd, 'd.' dd-MM-yyyy" },
lastYear: { type: "number", editable: false },
budget: { type: "number", editable: true },
fontcolor: { type: "text", editable: false }
}
}
},
batch: true
}
});
My Grid definition:
$("#budgetGrid").kendoGrid({
dataSource: gridDatasource,
toolbar: ["save","cancel"],
pageable: false,
editable: "inline",
navigatable: true,
groupable: false,
sortable: false,
pageable: false,
columns: [
{
field: "location",
hidden: true
},
{
field: "date",
title: "Day",
width: 180,
template: "<
span
style
=
'color:#=fontColor#'
>#= kendo.toString(date, 'dddd, dd-MM-yyyy') #</
span
>"
},
{
field: "lastYear",
title: "Sidste års oms.",
width: 80,
format: "{0:c0}",
attributes: { style: "text-align:right;" }
},
{
field: "budget",
title: "Budget",
width: 80,
format: "{0:c0}",
attributes: { style: "text-align:right;", class: "budgetamount" }
}
]
});
My backend controller actions:
[HttpPost]
public JsonResult GetBudgetData(string butik, int budgetyear, int budgetmonth)
{
return Json(db.GetDailyBudget(butik, new DateTime(budgetyear, budgetmonth, 1)));
}
[HttpPut]
public JsonResult PutBudgetData(IEnumerable<
DailyBudget
> budgets)
{
foreach (DailyBudget item in budgets)
{
// Do something....
}
return Json(null);
}
Either the update-routine of the grid doesn't fire or an error occurs in the kendo-all.js file...
I have tried all the tricks, tips and workarounds I can find or think of, but nothing seems to be working...
16 Answers, 1 is accepted
One thing that I notice in your code is that you declare the controller method for update with HttpPut verb, but in the Grid, you have configured the DataSource to make a POST request for updates. Try changing this to PUT.
Additionally, can you tell what is the error in the DataSource when the update method is hit? This could point us in the direction of the problem if the non-matching verbs are not the only issue.
Regards,
Tsvetina
Progress Telerik
Hi,
Thank you for the reply, but alas, nothing changed...
I have tried to remove the HttpPut predicate of my backend controller action...
But it seems that the grid does not invoke the update-function of the datasource anymore? Now I don't even get the error from the kendo-all-js anymore.
The readonly-oprion of the fields in the model-definition are also ignored by the grid...
Could you paste the error that you are getting when the Update method is hit? This should give us a better idea of the problem. Additionally, have you checked the parameterMap function to confirm that it produces well structured data?
One problem that I now notice with the Grid is that it uses "inline" editing, while the DataSource (and action method) is set to support batch editing. Note that batch editing works only with "incell" editing mode (demo).
This being said, you should either change the Grid editable property to "incell" or you should disable batch mode for the DataSource. If you disable batch editing, your action method should accept a single model:
[HttpPut]
public
JsonResult PutBudgetData(DailyBudget budget)
{
// save budget
return
Json(
null
);
}
If this does not help, it would be best if you can prepare a runnable sample and send it to us for further debugging.
Regards,
Tsvetina
Progress Telerik
Hi Tsvetina,
My problem is now a different problem...
The UPDATE function of the dataSource does not fire at all and neither the 'editable' or the 'format' property does not seem to get applied in the grid
My datasource definition is this:
var
gridDatasource =
new
kendo.data.DataSource({
batch:
true
,
transport: {
read: {
url:
"@Url.Action("
GetBudgetData
","
Dagsbudget
")"
,
dataType:
"json"
,
type:
"GET"
,
data:
{
butik: $(
"#location"
).data(
"kendoDropDownList"
).value(),
budgetyear:
new
Date($(
"#yearmonthpicker"
).data(
"kendoDatePicker"
).value()).getFullYear(),
budgetmonth:
new
Date($(
"#yearmonthpicker"
).data(
"kendoDatePicker"
).value()).getMonth() + 1
}
},
update: {
url:
"@Url.Action("
PutBudgetData
","
Dagsbudget
")"
,
type:
"POST"
,
contentType:
"application/json"
,
dataType:
"json"
},
parameterMap:
function
(options, operation) {
if
(operation !=
"read"
&& options.models) {
return
{ models: kendo.stringify(options.models) };
};
if
(operation ==
"read"
) {
return
options;
};
},
schema: {
model: {
id:
"Id"
,
fields: {
Id: { type:
"text"
, editable:
false
},
Location: { type:
"text"
, editable:
false
},
Date: { type:
"date"
, editable:
false
, format:
"dddd, 'd.' dd-MM-yyyy"
},
LastYear: { type:
"number"
, editable:
false
, format:
"{0:c}"
},
Budget: { type:
"number"
, editable:
true
, format:
"{0:c}"
},
FontColor: { type:
"text"
, editable:
false
}
}
}
}
}
});
My grid definition is like this:
$(
"#budgetGrid"
).kendoGrid({
dataSource: gridDatasource,
toolbar: [
"save"
,
"cancel"
],
pageable:
false
,
editable:
true
,
navigatable:
true
,
groupable:
false
,
sortable:
false
,
pageable:
false
,
columns: [
{
field:
"id"
,
hidden:
false
,
width: 100,
readonly:
true
},
{
field:
"location"
,
hidden:
true
},
{
field:
"Date"
,
title:
"Dato"
,
width: 180,
template:
"<span style='color:#=fontColor#'>#= kendo.toString(kendo.parseDate(date, 'yyyy-MM-dd'), 'dddd, dd-MM-yyyy') #</span>"
},
{
field:
"lastYear"
,
title:
"Sidste års oms."
,
width: 80,
format:
"{0:c0}"
,
attributes: { style:
"text-align:right;"
}
},
{
field:
"budget"
,
title:
"Budgetbeløb"
,
width: 80,
format:
"{0:c0}"
,
attributes: { style:
"text-align:right;"
, class:
"budgetamount"
}
}
]
});
and my back-end controller action is this:
[HttpPost]
public
ActionResult PutBudgetData(IEnumerable<DailyBudget> gridData)
{
string
result = string.empty();
// Do something
return
Json(null);
}
I have tried to use a different function outside of the grid/datasource:
<button id=
"saveButton"
>Save Data</button>
<script>
$(
"#saveButton"
).kendoButton({
icon:
"refresh"
,
click:
function
() {
var
budgetData = $(
"#budgetGrid"
).data().kendoGrid.dataSource.view();
$.ajax({
url:
"@Url.Action("
PutBudgetData
","
Dagsbudget
")"
,
type:
"POST"
,
contentTYpe:
"application/json"
,
type:
"POST"
,
data: {
gridData: JSON.stringify(budgetData);
},
success:
function
(result) {
alert(
"Data Saved"
);
},
error:
function
(xhr, status, error) {
alert(error);
}
});
}
});
</script>
This causes the backend-action to get hit, but the gridData-parameter is empty?
this seems to indocate to me, that there's a typo somewhere, but I simply cannot see it?
Any help is appreciated
OK, I found my typo:
I accidentally set the schema as part of the transporter.
After moving the schema out to the root of the grid-definition, I could get the update-function to fire and then get the error message from kendo.all.js.
the error is :
Unhandled exception at line 27, column 19104
in
http:
//localhost:9485/js/kendo.all.min.js
0x800a138f - Der opstod en JavaScript-kørselsfejl: Egenskaben
'data'
kan ikke hentes
for
reference, der er udefineret eller
null
occurred
(Roughly translated : "The property ' data' cannot be retrieved for a reference that is undefined or null")
The function in which this is occurring is this one:
01.
var
RemoteTransport = Class.extend({
02.
init:
function
(options) {
03.
var
that =
this
, parameterMap;
04.
options = that.options = extend({}, that.options, options);
05.
each(crud,
function
(index, type) {
06.
if
(
typeof
options[type] === STRING) {
07.
options[type] = { url: options[type] };
08.
}
09.
});
10.
that.cache = options.cache ? Cache.create(options.cache) : {
11.
find: noop,
12.
add: noop
13.
};
14.
parameterMap = options.parameterMap;
15.
if
(isFunction(options.push)) {
16.
that.push = options.push;
17.
}
18.
if
(!that.push) {
19.
that.push = identity;
20.
}
21.
that.parameterMap = isFunction(parameterMap) ? parameterMap :
function
(options) {
22.
var
result = {};
23.
each(options,
function
(option, value) {
24.
if
(option
in
parameterMap) {
25.
option = parameterMap[option];
26.
if
(isPlainObject(option)) {
27.
value = option.value(value);
28.
option = option.key;
29.
}
30.
}
31.
result[option] = value;
32.
});
33.
return
result;
34.
};
35.
},
36.
options: { parameterMap: identity },
37.
create:
function
(options) {
38.
return
ajax(
this
.setup(options, CREATE));
39.
},
40.
read:
function
(options) {
41.
var
that =
this
, success, error, result, cache = that.cache;
42.
options = that.setup(options, READ);
43.
success = options.success || noop;
44.
error = options.error || noop;
45.
result = cache.find(options.data);
46.
if
(result !== undefined) {
47.
success(result);
48.
}
else
{
49.
options.success =
function
(result) {
50.
cache.add(options.data, result);
51.
success(result);
52.
};
53.
$.ajax(options);
54.
}
55.
},
56.
update:
function
(options) {
57.
return
ajax(
this
.setup(options, UPDATE));
58.
},
59.
destroy:
function
(options) {
60.
return
ajax(
this
.setup(options, DESTROY));
61.
},
62.
setup:
function
(options, type) {
63.
options = options || {};
64.
var
that =
this
, parameters, operation = that.options[type], data = isFunction(operation.data) ? operation.data(options.data) : operation.data;
65.
options = extend(
true
, {}, operation, options);
66.
parameters = extend(
true
, {}, data, options.data);
67.
options.data = that.parameterMap(parameters, type);
68.
if
(isFunction(options.url)) {
69.
options.url = options.url(parameters);
70.
}
71.
return
options;
72.
}
73.
});
- line no. 64
Hopes this helps - i s it something with my ParameterMap setup in the transporter-definition?
OK,
Now I have the update function working...apart from one tiny, but crucial thing:
The data from the grid does not reach the back-end controller action?
the dataSource now looks like this:
var
gridDatasource =
new
kendo.data.DataSource({
batch:
true
,
transport: {
read: {
url: geturl,
dataType:
"json"
,
contentType:
"application/json"
,
type:
"GET"
,
data:
{
butik: $(
"#location"
).data(
"kendoDropDownList"
).value(),
budgetyear:
new
Date($(
"#yearmonthpicker"
).data(
"kendoDatePicker"
).value()).getFullYear(),
budgetmonth:
new
Date($(
"#yearmonthpicker"
).data(
"kendoDatePicker"
).value()).getMonth() + 1
}
},
update: {
url: saveurl,
type:
"POST"
,
//contentType: "application/json",
//dataType: "json"
},
parameterMap:
function
(data, type) {
if
(type ==
"read"
) {
return
data;
};
if
(type = !
"read"
&& data.models) {
return
kendo.stringify(data.models);
};
},
},
change:
function
(e) {
recalculateOverview();
},
error:
function
(e, jqxhr) {
console.log(e, jqxhr);
alert(jqxhr.ResponseText);
},
schema: {
model: {
id:
"id"
,
fields: {
id: { type:
"text"
, editable:
false
},
location: { type:
"text"
, editable:
false
},
date: { type:
"date"
, editable:
false
, format:
"dddd, 'd.' dd-MM-yyyy"
},
lastYear: { type:
"number"
, editable:
false
, format:
"c0"
},
budget: { type:
"number"
, editable:
true
, format:
"c0"
},
fontColor: { type:
"text"
, editable:
false
}
}
}
}
});
My Grid definition :
$(
"#budgetGrid"
).kendoGrid({
dataSource: gridDatasource,
toolbar: [
"save"
,
"cancel"
],
editable:
true
,
navigatable:
true
,
columns: [
{
field:
"id"
,
hidden:
true
,
},
{
field:
"location"
,
hidden:
true
},
{
field:
"date"
,
title:
"Dato"
,
width: 180,
template:
"<span style='color:#=fontColor#'>#= kendo.toString(kendo.parseDate(date, 'yyyy-MM-dd'), 'dddd, dd-MM-yyyy') #</span>"
},
{
field:
"lastYear"
,
title:
"Sidste års oms."
,
width: 80,
format:
"{0:c0}"
,
attributes: { style:
"text-align:right;"
}
},
{
field:
"budget"
,
title:
"Budgetbeløb"
,
width: 80,
format:
"{0:c0}"
,
attributes: { style:
"text-align:right;"
,
"class"
:
"budgetamount"
}
}
],
dataBound:
function
(e) {
//recalculateOverview();
}
});
and finally, my backend-action (unchanged):
[HttpPost]
public
ActionResult PutBudgetData(IEnumerable<DailyBudget> gridData)
{
foreach
(var item
in
gridData)
{
//Do stuff with the data...
}
return
Json(
null
);
}
Is there anybody out there who can tell me, where I've gone wrong?
The Action method is not fired, because the framework is not able to match the post data to the parameters for some reason. We could not be sure why, until you show the exact query that is sent to the server.
If you send us the query we will able to give you more specific information.
Meanwhile, you could try to specify the type in the parameters as List<DailyBudget> gridData, instead IEnumerable<DailyBudget> gridData
Additional option is to follow the Demo that Tsvetina refereed to. This approach is tested and proved to be working. Set the dataType to jsonp. Modify the parameterMap as follows:
parameterMap:
function
(data, type) {
if
(type ==
"read"
) {
return
data;
}
if
(options.models) {
return
{models: kendo.stringify(options.models)};
}
}
And here is the Action method used for the demo, it is defined without parameters, and then DeserializeObject method is used to access and deserialize the "models" property.
https://github.com/telerik/kendo-ui-demos-service/blob/master/KendoCRUDService/Controllers/ProductsController.cs#L21
Such approach also give you the ability to place breakpoint into the Action to debug it if something is not correct. The action should be fired even if the parameters are not correct, since it is defined without parameters.
Regards,
Vasil
Progress Telerik
Hi Vasil,
Thank you for your suggestions, but that did not Work.
However, the ContentLength to the backend action DID containt data, and the i hade to manually map the content from the POST into my data, so by addign this code to my controller-action...
Stream req = Request.Body;
string
json =
new
StreamReader(req).ReadToEnd();
gridData = JsonConvert.DeserializeObject<List<DailyBudget>>(json);
.. I got data that I could manipulate.
So for now, ut's working, and my gues is that it's the MVC5 mapper, that's messing things up...
The backend-object has properties defined with a starting capital-letter, but the data received by grid-datasource id not with a starting capital-letter...
Anyways, the issue is resolved and I am happy :)
Thank you for your patience...
I am glad that you have resolved the issue.
I will place some more information here for your latest findings so it could help others as well.
When using the .NET Core, the capital-letter/cammelCase issue can be caused by the breaking change that Microsoft made in the default JSON serializer.
This can be configured in the startup.cs of the application:
public
void
ConfigureServices(IServiceCollection services)
{
...
// Maintain property names during serialization. See:
services
.AddMvc()
.AddJsonOptions(options => options.SerializerSettings.ContractResolver =
new
DefaultContractResolver());
// Add Kendo UI services to the services container
services.AddKendo();
}
Regards,
Vasil
Progress Telerik
Thank you, Vasil,
But after implementing that change, and fixing the JavaScript code back to capital-letter, I am now BACK at the original JavaScript error:
Unhandled exception at line 27, column 20138
in
http:
//localhost:9486/js/kendo.all.min.js
0x800a138f - Der opstod en JavaScript-kørselsfejl: Egenskaben
'data'
kan ikke hentes
for
reference, der er udefineret eller
null
occurred
translated roughly as "Property 'data' cannot be read for reference that is undefined or 'NULL' occurred...
This only occurs when invoking the update-function of the transporter to send updated data back to the Controller action, which it never reaches so I cannot check the Action itself.
The read function of the transporter works as a charm.
Dont know where to go from here..:(
Do you think that you can prepare a sample project, where we can reproduce the issue? This will make it possible for us to debug it and find the cause of the issue much faster. You can replace the actual data returned by the Read method with a few mocked items to be able to isolate the problem.
Regards,
Tsvetina
Progress Telerik
Hi Tsvetina,
I have prepared a VS2017 project that will run without access to the database (which is actually located in Greenland :)
But I cannot attach it because it's around 8MB and there's a limit of 2MB - even with the entire kendo-library removed...
Is there som other way, that I can share this project with you?
You can open a support ticket from your Telerik account, there the limit on the attachment is 20MB. Alternatively, if your project does not contain sensitive information, you could upload it to a file sharing service or a cloud storage provider of your choice and send us a link.
Regards,
Tsvetina
Progress Telerik
The ZIP-file can be accessed here: https://1drv.ms/u/s!AseHPSqmtv3Tgew1RvXwKKm4T27dYg
if you have questions, you can reach me at the phone-number on my profile...
Again - thank you for your effort and commitment to this, especially since I suspect this to be a configuration-error on my part
In the project you sent me, the problem was caused by the casing of the "id" field setting in the DataSource schema:
schema: {
model: {
id:
"id"
,
// => should be Id
Another problem that could lead to more problems with the Grid editing is that all Id values came back as null from the server. I assume that you missed that only when preparing the sample data for the project and that your actual project has values for the items Id field. In order for CRUD operations to work without errors or glitches, it is required that there is an Id field declared which:
- has non-null values for all data items
- has unique value for each data item
Let me know if there are any issues remaining after you change the Id field casing in the schema definition.
Regards,
Tsvetina
Progress Telerik
Hi, Tsvetina,
In the regular datasource the ID does have a value, its just a missed property when I created the mock-up data function.
Thank you - Like I said - an Transporter Configuration Error on my part :) (or as we say in Denmark - an Error 40)
I fixed it in the transporter, and now the Update-function fires as expected
The incoming object in the backend controller (griddata) is stil empty though, but like I wrote earlier I'll address this to be in an error in the deserialization of MVC5.
I think we should close the issue here, since you have given me ALL the answers I needed and more....
Thank you very much for your patience and commitment.