Change data sent to server for related data on update using kendo.datasource? - kendo-ui

I am using the $expand to get related data which works fine but I need to change the data that is sent back
to the server when the data is updated
Example if my Server Side Data Model contains two entities
Contact
ID: number
firstName: string
middleName: string
lastname: string
ContactType: ContactType n-1
ContactType
ID: nubmer
name: string
ContactCollection: ContactType 1-n
Here is my datasource code
function GetContactDS(){
var MyModel = kendo.data.Model.define({
id: "ID",
fields: {
__KEY: { type: "string" },
__STAMP: { type: "number" },
ID: { editable: false, nullable: true },
firstName: { type: "string" },
middleName: { type: "string" },
lastName: { type: "string" }
},
});
var crudServiceBaseUrl = "http://127.0.0.1:8081/cors/Contact";
var MyDataSource = new kendo.data.DataSource({
transport: {
read: function(options) {
$.ajax( {
url: crudServiceBaseUrl + '/?$expand=ContactType',
dataType: "json",
data: options.data,
success: function(result) {
options.success(result);
}
});
},
update: function(options) {
$.ajax( {
url: crudServiceBaseUrl + "/?$method=update",
type: "POST",
dataType: "json",
data: kendo.stringify(options.data.models),
success: function(result) {
// notify the DataSource that the operation is complete
options.success(result);
}
});
},
destroy: {
url: crudServiceBaseUrl + "/?$method=delete",
type: "GET"
},
create: {
url: crudServiceBaseUrl + "/?$method=update",
dataType: "json",
type: "POST"
},
parameterMap: function(options, operation) {
if (operation !== "read" && options.models) {
return JSON.stringify({"__ENTITIES": options.models});
}
}
},
batch: true,
pageSize: 30,
schema: {
model: MyModel,
data: "__ENTITIES"
}
});
return MyDataSource;
}
The read request returns this data
{"__entityModel":"Contact","__COUNT":1,"__SENT":1,"__FIRST":0,"__ENTITIES":[{"__KEY":"7","__STAMP":9,"ID":7,"firstName":"jay","middleName":"a","lastName":"blue","ContactType":{"__KEY":"2","__STAMP":4,"ID":2,"name":"Home","contactCollection":{"__deferred":{"uri":"/rest/ContactType(2)/contactCollection?$expand=contactCollection"}}}}]}
Here is the code calling read and binding to grid
var ContactDS = GetContactDS();
$("#grid").kendoGrid({
selectable: "row",
filterable: true,
pageable: true,
sortable: true,
change: function(){
datamodel = this.dataItem(this.select());
ID = datamodel.ID
},
dataSource: ContactDS,
columns: [
{ field: "ID" },
{ field: "firstName" },
{ field: "middleName" },
{ field: "lastName" },
{field: "ContactType.name"}
]
});
Which works fine I am getting the expanded info for ContactType in my datasource and it binds to a grid fine.
Now I want to update the after it the selected data row is read into a form, reading the data into the form works fine.
The problem is sending the update back to the server which expects a slightly different format for the related entity ContactType
It only needs the changed value of "__Key" to update
Here is my update function:
$("#update").click(function () {
datamodel.set("firstName", $("#firstName").val());
datamodel.set("lastName", $("#lastName").val());
datamodel.set("middleName", $("#middleName").val());
// datamodel.set("ContactType.__KEY",3);
ContactDS.sync();
Here is the data that the server expects
{ "__ENTITIES": [{"__KEY":"7","__STAMP":14,"firstName":"jay","middleName":"a","lastName":"red","ContactType":{"__KEY":"2"}}]}
Here is what kendo.datasource is sending
[{"__KEY":"7","__STAMP":12,"ID":7,"firstName":"jay","middleName":"a","lastName":"blue","ContactType":{"__KEY":"3","__STAMP":2,"ID":3,"name":"Work","contactCollection":{"__deferred":{"uri":"/rest/ContactType(3)/contactCollection?$expand=contactCollection"}}}}]
So how do I either reformat the data or define my model or datasource options to make sure that the extra ContactType fields are removed just leaving the updated "_KEY:" as well as wrapping the whole request in { "_ENTITIES":}
Thanks for any help!
Dan

You could try to use the parameterMap function to format the data in the way you need.

I think I found the answer from this post
It explains a little more on using parameterMap. If you look at the kendoui docs on parameterMap it seems to indicate that this is only for managing parameters like
pageIndex,size,orderBy ect. But from the above post it shows you how to delete a related entity or you can just delete or modify the fields of an entity or related entity
Example I can delete just the ContactTypeID of the related entity ContactType
parameterMap: function(options, operation) {
if (operation == "create") {
return JSON.stringify({"__ENTITIES": options.models});
}
else if (operation == "update") {
debugger;
delete options.models[0].ContactType.ID;
return JSON.stringify({"__ENTITIES": options.models});
}
}
Still have some work to do but I think this will get me there
Thanks Pechka for your help

Related

Why parameterMap is not calling in KendoGrid?

have the following kendo grid:
this.kGrid = $element.kendoGrid({
columns: [
{ field: 'Title' },
{ field: 'Description' },
{
field: 'StartDate',
title: 'Start Date',
template: `#= kendo.toString(kendo.parseDate(DistributionDate), "${DATE_FORMAT} ${TIME_FORMAT}")#`,
}
],
filterable: true,
dataSource: {
type: 'aspnetmvc-ajax',
transport: {
read: {
url: '/Pubs/GetPubs',
dataType: "json",
type: 'POST',
data: () => ({
collectionId: this.selectedCollection,
categoryUid: this.selectedCategory
})
},
parameterMap: function (data, type) {
//i wawnt to add some logic here to convert StartDate from local time to UTC
}
},
serverFiltering: true,
schema: {
data: 'Data',
total: 'Total',
model: {
id: 'Uid',
fields: {
Title: { type: 'string' },
Description: { type: 'string' },
StartDate: { type: 'date' }
}
}
}
}
}).data('kendoGrid');
So i have a grid with 3 columns and all of them are filterable. My goal is to convert the date in the filter for StartDate from local time to UTC. That's why i added parameterMap handler. But for some reason, parameterMap method is not called at all. What i'm doing wrong here?
Here is full code snippet - https://dojo.telerik.com/IqadArAn/3. But the problem is not reproducible here
EDIT: It seems that problem is in dataSource type, which is 'aspnetmvc-ajax'. If to change the dataSource type, parameterMap works.
The alternative solution is to:
Skip setting the transport type
Implement custom read method
This questions describes the similar issue - https://www.telerik.com/forums/datasource-transport-function-mode-read-and-wrong-request-parameters.
Here is the code of custom read method, which should be placed inside of dataSource.transport.
read: (optionsData) => {
if (optionsData.data.filter) {
optionsData.data.filter.filters.forEach((item) => {
if (item.field === "StartDate") {
item.value = item.value.toISOString();
}
});
}
var data = kendo.data.transports["aspnetmvc-ajax"].prototype.options.parameterMap.call({ options: { prefix: "" }}, optionsData.data, "read", false);
data.encryptedCollectionId = this.selectedCollection;
data.categoryUid = this.selectedCategory;
$.ajax({
type: "POST",
url: 'Pubs/GetPubs',
data: data
})
.then(
function succes(res) {
optionsData.success(res);
},
function failure(res) {
optionsData.error(res);
}
);
}

Change Kendo Grid Cell With Ajax Response When Another Cell Changes

Using a Kendo grid with 3 columns, I have an event that fires when the first column is changed that makes an ajax call and returns some data. I want to update the second column with the returned data but I'm not having any luck and I'm not even sure if this is the correct approach. I can change the second column with static data by adding a change event to my datasource of my grid, but that of course doesn't help. The only examples I can seem to find show changing another column with client side data, not data returned from the server. Here's what I have so far:
$("#manualStatsGrid").kendoGrid({
dataSource: this.GetManualStatisticsDataSource(),
sortable: true,
pageable: false,
filterable: true,
toolbar: ["create"],
editable: "inline",
messages: {
commands: {
create: "Add New Statistic"
}
},
edit: function (e) {
var _this = _manualStats;
var input = e.container.find(".k-input");
var value = input.val();
input.keyup(function(){
value = input.val();
});
$("[name='Statistic']", e.container).blur(function(){
var input = $(this);
$("#log").html(input.attr('name') + " blurred " + value);
//valid the GL account number
$.ajax({
type: "GET",
url: _this.ValidateGlUrl,
dataType: 'json',
data: { glNumber: value },
success: function (response) {
var newDescription = response.Data.description;
console.log(newDescription);
//change description column here?
},
error: function (response) {
console.log(response);
}
});
});
},
columns: [
{ field: "Statistic" },
{ field: "Description" },
{ field: "Instructions" },
{ command: ["edit", "destroy"], title: " ", width: "250px"}
]
});
}
this.GetManualStatisticsDataSource = function () {
var _this = _manualStats;
var dataSource = {
type: "json",
transport: {
read: {
type: "POST",
url: _this.GetManualStatisticsUrl,
dataType: "json"
},
update: {
type: "POST",
url: _this.UpdateManualStatisticsUrl,
dataType: "json"
},
create: {
type: "POST",
url: _this.CreateManualStatisticsUrl,
dataType: "json"
},
destroy: {
type: "POST",
url: _this.DeleteManualStatisticsUrl,
dataType: "json"
}
},
schema: {
model: {
id: "Statistic",
fields: {
Statistic: {
type: "string",
editable: true,
validation: { required: true, pattern: "[0-9]{5}.[0-9]{3}", validationmessage: "Please use the following format: #####.###" }
},
Description: { editable: false },
Instructions: { type: "string", editable: true }
}
}
}
Inside the edit event, you have e.model. The model has the method set() which can change any dataItem's property value:
edit: function (e) {
...
var editEvent = e; // Creates a local var with the edit's event 'e' variable to be available inside the 'blur' event
$("[name='Statistic']", e.container).blur(function() {
...
$.ajax({
...
success: function(e, response) { // 'e' inside this callback is the 'editEvent' variable
e.model.set("Description", response.Data.description); // e.model.set() will change any model's property you want
}.bind(null, editEvent) // Binds the 'editEvent' variable to the success param
});
});
Working demo
Made this snippet of top of my head. Tell me if there is something wrong with it.

Kendo UI, Grid, modify Data before send

Is it possible to access and modify data in Kendo UI grid before updating?
Below is an example to illustrate what I need. The options.data contains the sent data but it is already formatted in string "models=%B7%22Id22%.... etc" not really convenient form.
dataSource = new kendo.data.DataSource({
transport: {
read: {
...
},
update: {
url: baseURL + "update",
beforeSend: function(xhr, options){
xhr.setRequestHeader('API-KEY', apikey );
var modifiedData = doSomething(options.data);
return modifiedData;
},
dataType: "json",
method: "POST",
dataFilter: function(data){
... some data recieved modification
return JSON.stringify(somedata);
},
complete: function(e) {
....
}
},
You should be able to use the parameterMap function, check the type for "update" and change the options.data anyway you want.
parameterMap: function(options, type) {
if(type === "update") {
options.someProperty = "somenewvalue";
}
return kendo.data.transports.odata.parameterMap(options, type);
}

Specifying the fields to send on an 'update' command using the Kendo UI Framework Data Source

I am using the Data Source object to connect to a SharePoint 2013 ODATA source using REST and then use this as the data for a Kendo UI Grid.
The Data Source reads the list correctly and populates the grid, but when I update an item in the Kendo UI Grid the following error is returned by the REST end point.
The property '__deferred' does not exist on type 'SP.SecurableObject'.
Make sure to only use property names that are defined by the type.
This is caused by the Data Source returning all the properties from the initial read request and then returning in the update command.
SharePoint returns __deferred properties with a REST URL to defer loading, but is throwing a wobbly if they are returned back in an update command request.
Below is my Data Source
var dataSource = new kendo.data.DataSource({
type: "odata",
transport: {
read: {
url: listUrl,
type: "GET",
dataType: "json",
contentType: "application/json;odata=verbose",
headers: {
"accept": "application/json;odata=verbose"
}
},
create: {
url: listUrl,
type: "POST",
dataType: "json",
contentType: "application/json;odata=verbose",
headers: {
"accept": "application/json;odata=verbose",
"X-RequestDigest": $("#__REQUESTDIGEST").val(),
}
},
update: {
url: function (data) {
return listUrl + "(" + data.ID + ")";
},
beforeSend: function (jqXhr, options) {
var data = JSON.parse(options.data);
jqXhr.setRequestHeader("If-Match", data.__metadata.etag);
},
type: "POST",
dataType: "json",
contentType: "application/json;odata=verbose",
headers: {
"accept": "application/json;odata=verbose",
"X-RequestDigest": $("#__REQUESTDIGEST").val(),
"X-HTTP-Method": "MERGE"
},
},
destroy: {
url: function (data) {
return listUrl + "(" + data.ID + ")";
},
type: "DELETE",
dataType: "json",
contentType: "application/json;odata=verbose",
headers: {
"accept": "application/json;odata=verbose",
"X-RequestDigest": $("#__REQUESTDIGEST").val(),
"X-HTTP-Method": "MERGE",
"If-Match": "*"
}
}
},
pageSize: 20,
schema: {
data: "d.results",
model: {
id: "ID",
fields: {
ID: { editable: false, nullable: false },
Title: { validation: { required: true } },
Body1: { validation: { required: true } },
Votes: { type: "number", validation: { required: true, min: 1 } },
}
}
}
});
You can specify a "parameterMap" function on the DataSource.transport, and filter out the data you don't want.
var dataSource = new kendo.data.DataSource({
type: "odata",
transport: {
parameterMap: function(data, type){
if (type === "update" && data["__deferred"]){
delete data["__deferred"];
}
return kendo.stringify(data);
}
// ...
},
// ...
});
See http://docs.kendoui.com/api/framework/datasource#configuration-transport.parameterMap
Another option, if you're working with observable objects, is to provide a custom .toJSON method on your object. I wrote up a blog post about this, here: http://www.kendoui.com/blogs/teamblog/posts/13-04-04/hijacking-tojson-for-fun-and-profit.aspx
Using Derick Bailey's answer I was able to do the following with the parameterMap
the question object is a defined data model. Using this method, it will only send over the fields defined in the model plus the __metadata field that is need by SharePoint.
parameterMap: function (data, type) {
if (type == "update") {
for (var property in data) {
if (property != "__metadata" && !question.fields[property])
delete data[property];
}
}
return kendo.stringify(data);
}

Load data into Highcharts with Ajax

I am trying to update high charts on page load and on select menu change with JQUERY AJAX call. There is data being returned in [[10,1228800000],[10,1228800000]] format.The chart is blank and does not graph any of the data.
Tried several solutions posted on here but none worked.
var chart;
$(document).ready(function() {
var options = {
chart: {
renderTo: 'stats',
defaultSeriesType: 'spline'
},
title: {text:''},
xAxis: {
type: 'datetime'
},
yAxis: {},
series: []
};
var month = 'July';
$.ajax({
type: "POST",
data: "month="+month,
url: "update_visits_chart",
success: function (data) {
options.series.push(data);
chart = new Highcharts.Chart(options);
}
});
Any errors? thanks in advance.
EDIT:
MOST RECENT CODE STILL NOT WORKING:
var options = {
chart: {
renderTo: 'stats',
type: 'spline'
},
title: {
text: ''
},
xAxis: {
type:'datetime',
tickInterval: 30 * 24 * 3600 * 1000,
dateTimeLabelFormats: {
day: '%b %e'
},
labels: {
rotation: -45,
align: 'right'
}
},
yAxis: {
title: {
text: 'Number of visits'
},
min: 0
},
tooltip: {
formatter: function() {
return Highcharts.dateFormat('%b %e', this.x) +'<br />'+this.y+' visit(s)';
}
},
legend: {
enabled: true
},
credits: {
enabled: false
},
exporting: {
enabled: false
},
series: [{
name: 'Number of Visits',
data: []
}]
};
var month = 'July';
$.ajax({
type: "POST",
url: "update_visits_chart",
data: "month="+month,
success: function(data){
options.series[0].data = data;
chart = new Highcharts.Chart(options);
}
});
You have to use the setData methode of the series object as descriped in documentation. In your case it is options.series[0].setData(Data)
And I think you have to turn your Ajax result from string to a real object/array by using JSON.parse(data).
EDIT:
#Ricardo Lohmann: in the ajax-call he did not specify the dataType he expects in the response, so jQuery will guess the dataType. But it will not recognize a string starting with [ as JSON and I doubt his response will be served with correct mime type application/json. So specifying the correct mime type should also solve the problem. But I do not have an example of the complete ajax respons of the questioner. So I'm just guessing, too.
I'd recommend the following ajax call:
$.ajax({
type: "POST",
url: "update_visits_chart",
data: {month: month},
dataType: 'json',
success: function(data){
options.series[0].setData(data);
}
});
#Jugal Thakkar
$.getJSON is just a shortcut for the ajax-call above, but it is less flexible because you have less options.
You have to set directly data to series because data is already a multidimensional array.
The following code will fix it.
Change options.series.push(data); to options.series = data;

Resources