Insert in DB Issue, using OData and Kendo Grid - sql

I'm using a Kendo Grid with a datasource using type Odata.
I have troubles creating a new row in the database from the datasource.
This is my datasource code:
var ds = new kendo.data.DataSource({
type: "odata",
transport: {
read: {
url: baseUrl,
dataType: "json"
},
update: {
url: function (data) {
return baseUrl + "('" + data.ID_Agenzia + "')";
}
},
create: {
url: baseUrl
},
destroy: {
url: function (data) {
return baseUrl + "('" + data.ID_Agenzia + "')";
}
}
},
schema: {
model: {
id: "ID_Agenzia",
fields: {
ID_Agenzia: { type: "string" },
// etc... my other fields omitted for brevity.
}
}
},
pageSize: 20,
serverPaging: true,
serverFiltering: true,
serverSorting: true
});
Then I tried a simple grid with the automatic toolbar Create (pretty standard, I think I can omit the code), using this DS.
As far as I understood, Kendo got a method "isNew" to discern between Create/Update and it checks if the ID is === to the default value.
All the examples I found googling around, were using the ID as a numeric incremental value... But in my table the ID is a String (obviously unique) that needs to be inserted by the user!!
Hoping I've explained myself well, the issue should be clear: If the user inserts the ID, the datasource won't recognize that it's a Create operation...
Otherwise if I forbit the manual ID insert, the create will work... but the row will be inserted in the DB with the default value (empty string) and this is wrong!
How can I solve this?
Thanks.
[EDIT] Addictional info:
I'm using latest version of Kendo-ui and Odata 2.0

Related

How to change the columns collection set of a kendo TreeList dynamically?

Try to change the columns list dynamically via a query ...
When I construct the TreeList, I call for columns :
$("#treelist").kendoTreeList({
columns: AnalyseCenterSKUService.getKPIColumnList($scope)
If I return a simple array with the fields, it's working ..
If I call a $http.get (inside my getKPIColumnList(..) function) which add some columns to the existing array of columns, the TreeList is not constructed correctly.
Any suggestion will be really appreciated ! :)
EDIT 22-10-2019 09:00
Treelist init
$("#treelist").kendoTreeList({
columns: AnalyseCenterSKUService.getKPIColumnList($scope),
scrollable: true,
columnMenu : {
columns : true
},
height: "100%",
dataBound: function (e) {
ExpandAll();
},
dataSource: {
schema: {
model: {
id: "id",
parentId: "parentId",
fields: {
id: { type: "number" },
parentId: { type: "number", nullable: true },
fields: {
id: { type: "number" },
parentId: { type: "number", nullable: false }
}
}
}
},
transport: {
read: {
url: "/api/AnalyseCenter/GetWorkOrderTree/0",
dataType: "json"
}
}
}
The getKPIColumnList return an static array + some push with dynamic columns (from DB)
angular.module('AnalyseCenterDirectives')
.service ('AnalyseCenterSKUService', function ($http) {
var toReturn = [ {field: "Name", title: "Hiérachie SKU", width: "30%" }, ..., ..., .... ];
I try in this function to push DB result
return $http.get("/api/AnalyseCenter/GetWorkOrderHistorianAdditonalColumns?equipmentName=" + equipmentName)
.then(function (result) {
var data = result.data;
if (data && data !== 'undefined') {
var fromDB = data;
angular.forEach(fromDB, function (tag) {
var tagName = tag.replace(".","_");
toReturn.push({
field: tagName, title: tag, width: '10%',
attributes: { style: "text-align:right;"} })
})
The stored procedure GetWorkOrderHistorianAdditonalColumns returns a list of string (future column)
That is because ajax is async, that means your tree list is being initialized before the request finishes. A classic question for JavaScript newcomers. I suggest you take a while to read about ajax, like How does AJAX works for instance.
Back to your problem. You need to create your tree list inside the success callback(I can't give you a more complete solution since I don't know what you're doing inside your function or which framework you're using to open that ajax request) with the result data, which is probably your columns. Then it would work as if you're initializing it with arrays.

Syncfusion TreeGrid and Grid with WebAPI doesn't work on delete

I've set up a treeGrid (the grid is the same) to get data through the ASP.NET WebAPI using their DataManager:
var categoryID=15;
var dataManager = ej.DataManager({
url: "/API/myrecords?categoryID=" + categoryID,
adaptor: new ej.WebApiAdaptor()
});
$("#treeGridContainer").ejTreeGrid({
dataSource: dataManager,
childMapping: "Children",
treeColumnIndex: 1,
isResponsive: true,
contextMenuSettings: {
showContextMenu: true,
contextMenuItems: ["add", "edit", "delete"]
},
contextMenuOpen: contextMenuOpen,
editSettings: { allowEditing: true, allowAdding: true, allowDeleting: true, mode: 'Normal', editMode: "rowEditing" },
columns: [
{ field: "RecordID", headerText: "ID", allowEditing: false, width: 20, isPrimaryKey: true },
{ field: "RecordName", headerText: "Name", editType: "stringedit" },
],
actionBegin: function (args) {
console.log('ActionBegin: ', args);
if (args.requestType === "add") {
//add new record, managed manually...
var parentID = 0;
if (args.level != 0) {
parentID = args.parentItem.TaxonomyID;
}
args.data.TaxonomyID = 0;
addNewRecord(domainID, parentID, args.data, args.model.selectedRowIndex);
}
}
});
The GET works perfectly.
The PUT works fine as I'm managing it manually because it's not called at all from the DataManager, and in any case I want to manage the update of the records in the TreeGrid.
The problem is with DELETE, that is called by the DataManager when I click Delete from the context menu over an item in the TreeGrid.
It makes a call to the following URL:
http://localhost:50604/API/myrecords?categoryID=15/undefined
and obviously, I get a 405 (Method Not Allowed)
The problem is given by the categoryID parameters that break the RESTful schema, and the DataManager is not able to understand that there is a parameter.
A possible solution could be to send this parameter as a POST variable but the DataManager is not able to do it.
Does anyone have a clue of how to solve it? it's a common scenario in real-world applications.
While populating Tree Grid data using ejDataManger, CRUD actions will be handled using inbuilt Post (insert), Put (update), Delete requestType irrespective of CRUD URL’s. So, no need to bind ‘removeUrl’ for deleting records.
And, in the provided code example parameter is passed in the URL to fetch data hence the reported issue occurs. Using ejQuery’s addParams method we can pass the parameter in URL. You can find the code example to pass the parameter using Tree Grid load event and the parameter is retrieved in server side using DataManager.
[html]
var dataManager = ej.DataManager({
url: "api/Values",
adaptor: new ej.WebApiAdaptor()
});
$("#treeGridContainer").ejTreeGrid({
load: function (args) {
// to pass parameter on load time
args.model.query.addParams("keyId", 48);
},
});
[controller]
public object Get()
{
var queryString = HttpContext.Current.Request.QueryString;
// here we can get the parameter during load time
int num = Convert.ToInt32(queryString["keyId"]);
//..
return new {Items = DataList, Count = DataList.Count() };
}
You can find the sample here for your reference.
Regards,
Syncfusion Team

breezejs addEntityType issue

I'm new to breezejs. I am trying to define my entity type in the client without getting metadata from the server. I have a property called ID in the server entity.
I've defaulted the naming convention in the client side to camel case using the following code.
breeze.NamingConvention.camelCase.setAsDefault();
so, I started to map the entity as follows
store.addEntityType({
shortName: "Photo",
namespace: "MyProj.Models",
dataProperties: {
id: {
dataType: DataType.Guid,
isNullable: false,
isPartOfKey: true
},
title: {
dataType: DataType.String
},
description: {
dataType: DataType.String
},
createdDate: {
dataType: DataType.DateTime
},
}
});
This worked all fine, except the id field is not getting the proper value. instead, it has the default value set by the breeze datatype ctor which is equals to Guid.Empty.
by stepping through breezejs debug script, I found out that it looks for a property name called Id in the data that comes from the ajax request. But it can't find it as the property is ID so it initialize it to empty guid string. I assumed that by setting nameOnServer property of the dataProperty id, I will be able to fix it.
store.addEntityType({
shortName: "Photo",
namespace: "MyProj.Models",
dataProperties: {
id: {
dataType: DataType.Guid,
isNullable: false,
nameOnServer: 'ID',
isPartOfKey: true
},
title: {
dataType: DataType.String
},
description: {
dataType: DataType.String
},
createdDate: {
dataType: DataType.DateTime
},
}
});
But it didn't work.
Further digging through the breez.debug.js code, in the method updateClientServerNames on line 7154, it seems it ignores the nameOnServer that I have defined.
Am I missing something here?
Okay, Feel like I spent my whole life through breeze documentation. Anyways, Finally solved the issue. To be honest, this wasn't a problem in breeze (but I wonder why it doesn't override the actual nameOnServer when I provide one). It's an error made by one of the developers in the early stage of the database implementation (probably 6 years ago). If the database adhered to Pascal Case naming convention, things would have worked perfectly fine.
As a solution I wrote a custom naming convention which corrects the naming convention error when it has ID in the name and combines it with camelCase naming convention.
var createInconsistenIDConvention = function () {
var serverPropertyNameToClient = function (serverPropertyName, prop) {
if (prop && prop.isDataProperty && (prop.nameOnServer && prop.nameOnServer === "ID")) {
return "id";
} else {
var firstSection = serverPropertyName.substr(0, 1).toLowerCase();
var idSection = "";
if (serverPropertyName.substr(1).indexOf("ID") != -1) {
firstSection += serverPropertyName.substr(1, serverPropertyName.substr(1).indexOf("ID")).toLowerCase() + "Id";
} else {
firstSection += serverPropertyName.substr(1);
}
return firstSection;
}
}
var clientPropertyNameToServer = function (clientPropertyName, prop) {
if (prop && prop.isDataProperty && (prop.nameOnServer && prop.nameOnServer.indexOf("ID") != -1)) {
return prop.nameOnServer;
} else {
return clientPropertyName.substr(0, 1).toUpperCase() + clientPropertyName.substr(1);
}
}
return new breeze.NamingConvention({
name: "inconsistenID",
serverPropertyNameToClient: serverPropertyNameToClient,
clientPropertyNameToServer: clientPropertyNameToServer
});
};
Not sure if the way I've used nameOnServer property is not correct. I couldn't find any documentation on that in breeze website.
please note that the above code only consider situations like ID, CountryID, GameID, PersonID etc.
Problem solved for now.

Nested grid in ExtJS 4.1 using Row Expander

On the front-end I have a Calls grid. Each Call may have one or more Notes associated with it, so I want to add the ability to drill down into each Calls grid row and display related Notes.
On the back-end I am using Ruby on Rails, and the Calls controller returns a Calls json recordset, with nested Notes in each row. This is done using to_json(:include => blah), in case you're wondering.
So the question is: how do I add a sub-grid (or just a div) that gets displayed when a user double-clicks or expands a row in the parent grid? How do I bind nested Notes data to it?
I found some answers out there that got me part of the way where I needed to go. Thanks to those who helped me take it from there.
I'll jump straight into posting code, without much explanation. Just keep in mind that my json recordset has nested Notes records. On the client it means that each Calls record has a nested notesStore, which contains the related Notes. Also, I'm only displaying one Notes column - content - for simplicity.
Ext.define('MyApp.view.calls.Grid', {
alias: 'widget.callsgrid',
extend: 'Ext.grid.Panel',
...
initComponent: function(){
var me = this;
...
var config = {
...
listeners: {
afterrender: function (grid) {
me.getView().on('expandbody',
function (rowNode, record, expandbody) {
var targetId = 'CallsGridRow-' + record.get('id');
if (Ext.getCmp(targetId + "_grid") == null) {
var notesGrid = Ext.create('Ext.grid.Panel', {
forceFit: true,
renderTo: targetId,
id: targetId + "_grid",
store: record.notesStore,
columns: [
{ text: 'Note', dataIndex: 'content', flex: 0 }
]
});
rowNode.grid = notesGrid;
notesGrid.getEl().swallowEvent(['mouseover', 'mousedown', 'click', 'dblclick', 'onRowFocus']);
notesGrid.fireEvent("bind", notesGrid, { id: record.get('id') });
}
});
}
},
...
};
Ext.apply(me, Ext.apply(me.initialConfig, config));
me.callParent(arguments);
},
plugins: [{
ptype: 'rowexpander',
pluginId: 'abc',
rowBodyTpl: [
'<div id="CallsGridRow-{id}" ></div>'
]
}]
});

jqGrid getting data from url option

I am having the hardest time pulling data out of my url and putting it in my jqGrid in asp.net MVC4. What am I missing here?
$(document).ready(function () {
jQuery("#frTable").jqGrid ({
cmTemplate: { sortable: false },
caption: '#TempData["POPNAME"]' + ' Population',
url: '#Url.Action("GetAjaxPagedGridData", "Encounters", new { popId = TempData["POPULATIONID"] })',//'/Encounters/GetAjaxPagedGridData/'+ '',
datatype: "jsonstring",
mtype: 'GET',
pager: '#pager',
height: '450',
...
Then you go into the colNames and colModels and all that stuff which is tangential to this particular inquiry. Here is the methods that return my data. Suffice it to say that the stuff that I do to do client side paging seems to work. But I can't verify unless I can actually see the data?
What am I doing wrong here?
public string GetAjaxPagedGridData(int page, int rows, int popId) {
string userID = HttpContext.User.Identity.Name;
DataRepository dr = new DataRepository();
string encounter = dr.Get(popId, userID, "1", page, rows, "");
return encounter;
}
You can use postData option of jqGrid in the form
postData: {
popId: 123
}
or in the form
postData: {
popId: function () {
return 123;
}
}
You should use additionally datatype: "json" instead of datatype: "jsonstring".