webapi 2 key delta patch update - asp.net-web-api

Using le framework here
http://blog.longle.net/2014/03/04/harness-the-power-of-asp-net-mvc-web-api-odata-kendo-ui-requirejs-to-build-an-easy-maintainable-spa-for-the-net-developer-published/
and here
Web API + OData - PATCH request 400 error
how to send key delta in patch update of WebAPI 2 odata where kendo datasource "batch: true"
AcceptVerbs("PATCH", "MERGE")]
public async Task<IHttpActionResult> Patch([FromODataUri] int key, Delta<Company> patch)
The key is always empty!!!
Does WebAPI odata supported by kendo?

Since it is a very specific task, I have only one tweak that is working for Repository Pattern provided by Le framework shown in the above mentioned link.
define(['kendo', 'testModel'],
function (kendo, testModel) {
var svcUrl = '/odata/modelURL';
var ds_test = new kendo.data.DataSource({
type: 'odata',
transport: {
read: {
//async: false,
url: svcUrl,
dataType: 'json'
},
update: {
url: function (data) {
return svcUrl + '(' + data.models[0].ID + ')';
},
dataType: 'json',
type: 'PATCH'
},
create: {
url: function (data) {
return svcUrl + '(' + data.models[0].ID + ')';
},
dataType: 'json',
type: 'PATCH'
},
destroy: {
url: function (data) {
return svcUrl + '(' + data.models[0].ID + ')';
},
dataType: 'json',
type: 'PATCH'
},
parameterMap: function(data, operation) {
if (operation != 'read') {
var model = kendo.stringify(data.models[0]);
return model;
};
return data.models;
}
},
batch: true,
serverPaging: true,
serverSorting: true,
serverFiltering: true,
pageSize: 10,
schema: {
data: function (data) { return data['value']; }, //{ return data.value; },
total: function (data) { return data['odata.count']; },
model: testModel
//parse: function(response) {
// var f = ds_appl_home.options.schema.model.fields;
// $.each(response, function (key, value) {
// if (!(key.toString() in f)) {
// delete response[key];
// }
// });
// return response;
//}
},
error: function (e) {
...
}
});
return ds_test;
});

Related

How to make the method getItem from the offlineStorage of the Kendo Datasource work with LocalForage

I need to setup my datasource to use localForage to manage my offline data storage.
The problem I'm having is that localForage is asynchronous by nature, only allowing us to use a callback or a promise to retrieve data.
This is how my code is set-up:
(function () {
'use strict';
var serviceId = 'employeeDataSource';
angular.module('app').factory(serviceId, function () {
var crudServiceBaseUrl = 'Data/Employees';
var dataSource = new kendo.data.DataSource({
type: "odata",
offlineStorage: {
getItem: function () {
localforage.getItem('employees-key').then(function (value) {
console.log(value);
return JSON.parse(value);
});
},
setItem: function (item) {
if (item.length > 0) {
localforage.setItem("employees-key", JSON.stringify(item));
}
}
},
transport: {
read: {
url: crudServiceBaseUrl + "/",
dataType: "json"
},
update: {
url: function (data) {
return crudServiceBaseUrl + "(guid'" + data.EmployeeId + "')";
}
},
create: {
url: crudServiceBaseUrl
},
destroy: {
url: function (data) {
return crudServiceBaseUrl + "(guid'" + data.EmployeeId + "')";
}
}
},
batch: false,
pageSize: 5,
serverPaging: true,
schema: {
data: function (data) {
return data.value;
},
total: function (data) {
return data['odata.count'];
},
model: {
id: "EmployeeId",
fields: {
EmployeeId: { editable: false, nullable: true },
Name: { validation: { required: true } },
Email: { validation: { required: true } },
IsManager: { type: "boolean" },
MaxHolidaysPerYear: { editable: false, type: "number", validation: { min: 0, required: true } },
HolidaysLeftThisYear: { type: "number", validation: { min: 0, required: true } }
}
}
},
error: function (e) {
if (e.xhr.responseText !== undefined) {
console.log(e.xhr.responseText);
}
}
});
dataSource.online(navigator.onLine);
$(window).on("offline", function () {
dataSource.online(false);
});
$(window).on("online", function () {
dataSource.online(true);
});
return dataSource;
});
})();
When off-line, the getItem gets called, then the setItem gets call as well with an empty array, hence the:
if (item.length > 0) {
localforage.setItem("employees-key", JSON.stringify(item));
}
When the promise finally returns the off-line data (with the correct values I expected), the Grid displays no results.
This behaviour is presumably because of the promise ?
I tried the same thing with sessionStorage and its worked perfectly... i.e.:
getItem: function () {
return JSON.parse(sessionStorage.getItem("employees-key"));
},
setItem: function (item) {
sessionStorage.setItem("employees-key", JSON.stringify(item));
}
What can I do to get around this?
Just got a heads-up from Telerik
There is an issue logged in their GitHub repo about the same problem.
You can keep track on the progress here:
https://github.com/telerik/kendo-ui-core/issues/326

Kendo & Web API using OData: ODataActionParameters is null

Using OData v3, trying to bind Kendo Grid to OData Actions.
Problem
ODataActionParameters is always null...
Here are my actions:
[EnableQuery]
[HttpPost]
public virtual IQueryable<ComparitiveLocalizableString> GetComparitiveTable(ODataActionParameters parameters)
{
string cultureCode = (string)parameters["cultureCode"];
var query = Repository.Table
.Where(x => x.CultureCode == null || x.CultureCode == cultureCode)
.GroupBy(x => x.TextKey)
.Select(grp => new ComparitiveLocalizableString
{
Key = grp.Key,
InvariantValue = grp.FirstOrDefault(x => x.CultureCode == null).TextValue,
LocalizedValue = grp.FirstOrDefault(x => x.CultureCode == cultureCode) == null ? "" : grp.FirstOrDefault(x => x.CultureCode == cultureCode).TextValue
});
return query;
}
[HttpPost]
public virtual IHttpActionResult PutComparitive(ODataActionParameters parameters)
{
string cultureCode = (string)parameters["cultureCode"];
string key = (string)parameters["key"];
var entity = (ComparitiveLocalizableString)parameters["entity"];
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
if (!key.Equals(entity.Key))
{
return BadRequest();
}
var existing = Repository.Table.FirstOrDefault(x => x.CultureCode == cultureCode && x.TextKey == key);
if (existing == null)
{
return NotFound();
}
existing.TextValue = entity.LocalizedValue;
Repository.Update(existing);
return Updated(entity);
}
[HttpPost]
public virtual IHttpActionResult DeleteComparitive(ODataActionParameters parameters)
{
string cultureCode = (string)parameters["cultureCode"];
string key = (string)parameters["key"];
var entity = Repository.Table.FirstOrDefault(x => x.CultureCode == cultureCode && x.TextKey == key);
if (entity == null)
{
return NotFound();
}
Repository.Delete(entity);
return StatusCode(HttpStatusCode.NoContent);
}
My OData route configuration:
var getComparitiveTableAction = builder.Entity<LocalizableString>().Collection.Action("GetComparitiveTable");
getComparitiveTableAction.Parameter<string>("cultureCode");
getComparitiveTableAction.Returns<IQueryable<ComparitiveLocalizableString>>();
var putComparitiveAction = builder.Entity<LocalizableString>().Collection.Action("PutComparitive");
putComparitiveAction.Parameter<string>("cultureCode");
putComparitiveAction.Parameter<string>("key");
putComparitiveAction.Parameter<ComparitiveLocalizableString>("entity");
putComparitiveAction.Returns<IHttpActionResult>();
var deleteComparitiveAction = builder.Entity<LocalizableString>().Collection.Action("DeleteComparitive");
deleteComparitiveAction.Parameter<string>("cultureCode");
deleteComparitiveAction.Parameter<string>("key");
deleteComparitiveAction.Returns<IHttpActionResult>();
and the config for my Kendo Grid:
var odataBaseUrl = "/odata/cms/LocalizableStrings/";
$(document).ready(function () {
$("#Grid").kendoGrid({
data: null,
dataSource: {
type: "odata",
transport: {
read: {
url: odataBaseUrl + "GetComparitiveTable",
dataType: "json",
type: "POST"
},
update: {
url: odataBaseUrl + "PutComparitive",
dataType: "json",
type: "POST"
},
destroy: {
url: odataBaseUrl + "DeleteComparitive",
dataType: "json",
type: "POST"
},
parameterMap: function (options, operation) {
if (operation === "read") {
alert(cultureCode);
return JSON.stringify({
cultureCode: cultureCode
});
}
else if (operation === "update") {
var model = models[0];
return {
cultureCode: cultureCode,
key: model.Key,
entity: model
};
}
else if (operation === "destroy") {
var model = models[0];
return {
cultureCode: cultureCode,
key: model.Key
};
}
}
},
schema: {
data: function (data) {
return data.value;
},
total: function (data) {
return data["odata.count"];
},
model: {
fields: {
Key: { type: "string", editable: false },
InvariantValue: { type: "string", editable: false },
LocalizedValue: { type: "string" }
}
}
},
batch: true,
//etc......
Where am I going wrong? How can I fix this?
It seems I just needed to add, contentType: "application/json" as per the following:
read: {
url: odataBaseUrl + "GetComparitiveTable",
dataType: "json",
contentType: "application/json",
type: "POST"
},
update: {
url: odataBaseUrl + "PutComparitive",
dataType: "json",
contentType: "application/json",
type: "POST"
},
destroy: {
url: odataBaseUrl + "DeleteComparitive",
dataType: "json",
contentType: "application/json",
type: "POST"
}

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);
}

How to use ajaxStart if $.ajax method is defined in a class?

I have created ajax method in one class in my js file. Below is enclosed for the reference
var ajaxcall =
{
SitePath: '',
data: '',
url: '',
callbackfunction: '',
fileElementId: '',
AjaxRequest: false,
callback: true,
async: false,
folder: '',
filename: '',
Call: function () {
if (ajaxcall.AjaxRequest == true) {
alert(ajaxcall.AjaxRequest);
return;
}
else {
try {
ajaxcall.AjaxRequest == true;
alert('b');
$.ajax({
type: "POST",
url: ajaxcall.url,
data: ajaxcall.data,
contentType: "application/json; Characterset=utf-8",
dataType: "json",
async: false,
success: function (data) {
if (ajaxcall.callback == true) {
ajaxcall.callbackfunction(data);
}
},
error: function (request, status, error) {
//alert("Exception Handling : \n" + request.responseText);
alert('Unable to process the request at this moment! Please try again later.');
},
complete: function () {
ajaxcall.AjaxRequest = false;
}
});
}
catch (e) {
ajaxcall.AjaxRequest == false;
// alert("Error Catch : " + e.Description + '\n' + 'Message: ' + e.Message);
}
}
},
AjaxFileUpload: function () {
$.ajaxFileUpload({
type: "POST",
url: "../GenericHandlers/FileUploader.ashx?path=" + ajaxcall.folder,
dataType: 'json',
async: false,
secureuri: false,
fileElementClass: ajaxcall.fileElementClass,
success: function (data) {
var data = data.toString();
ajaxcall.filename = data.substring(6, data.length - 7);
alert(ajaxcall.filename);
return true;
}
});
}
};
Now i want to show a div when ajax call starts and hide after finish.
So for that i have used
$(document).ready(function(
$('#Loading').ajaxStart(function () {
alert('a');
$('#Loading').show();
}).ajaxStop(function () {
$('#Loading').hide();
});
});
But when i call the ajax method (defined above in the class), control goes into ajax method first then in ajaxStart.
I don't know why it is happening. Please help.
Use the recommended global for these:
$.ajaxStart(function() {
$("#Loading").show();
});
$.ajaxComplete(function() {
$("#Loading").hide();
});
Try it this way attached to your Loading id element:
$("#Loading").ajaxStart(function() {
$(this).show();
});
$("#Loading").ajaxComplete(function() {
$(this).hide();
});
AjaxStart called when the http request start, not when the ajax method executes.

asp.net mvc 3 json does not work

This is my jquery with json
$('#btnVerificationOk').click(function () {
var verId = $('#trans_verification_id').val();
var verCode = $('#trans_verification_code').val();
$.ajax({
url: '/Profile/CompleteTransactions',
type: 'POST',
data: { },
dataType: 'json',
success: function (result) {
alert(result);
},
error: function () {
alert('ERROR ERROR !!!!!!!!!!!!');
}
});
});
And My C# method:
[Authorize]
[HttpPost]
private JsonResult CompleteTransactions()
{
return Json("Done");
}
Its always alerts 'ERROR ERROR !!!!!!!!!!!!' i tried debugging but CompleteTransactions method is not firing
And this is my second json which is bellow and works good
$('#btnTransfareOk').click(function () {
var userName = $('#transfare_username').val();
var amount = $('#transfare_amount').val();
if (userName == '' || amount == '') {
$('#transfare_error_list').html('Please fill boxes.');
} else {
$.ajax({
url: '/Profile/TranfareMoney',
type: 'POST',
data: { ToUsername: userName, Amount: amount },
dataType: 'json',
success: function (result) {
$('#transfare_error_list').html(result.text);
$('#trans_verification_id').val(result.id);
$('#transfare_username').val("");
$('#transfare_amount').val("");
},
error: function () {
$('#transfare_error_list').html('Oops... Error.');
}
});
}
});
I'm not 100% sure, but shouldn't you controller action handler be public ?

Resources