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

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

Related

Is there buttons / funtion calls & tables handled in MetaWidget - js

I am wondering if some can help me out to get the buttons , function calls and tables displayed using metawidget jsonSchema.
Unfortunately, I can see it just as a form render-er for our applications, is it something which we need to define externally? also if we could navigate from one form to another somehow
<script type="text/javascript">
var mw = new metawidget.Metawidget( document.getElementById( 'metawidget' ), {
inspector: new metawidget.inspector.CompositeInspector( [ new metawidget.inspector.PropertyTypeInspector(),
function( toInspect, type, names ) {
return {
properties:
name: {
required: true
},
notes: {
type: "string",
large: true
},
employer: {
type: "string",
section: "Work"
},
department: {
type: "string"
}
}
};
} ] ),
layout: new metawidget.jqueryui.layout.TabLayoutDecorator(
new metawidget.layout.TableLayout( { numberOfColumns: 2 } ))
} );
mw.toInspect = person;
mw.buildWidgets();
Above schema holds only properties to render the fields, but where to specify the functionalities?
Assuming the object you are inspecting has functions, in JSON schema you can specify the property with a type of function. Metawidget will render these as buttons (and will wire up their click handler).
Note that PropertyTypeInspector will find functions automatically, so you could also consider combining your JsonSchemaInspector with a PropertyTypeInspector using CompositeInspector.
Often your data object (e.g. var person = { firstname: 'Homer', surname: 'Simpson' } ) and your actions object (e.g. var personActions = { save: function() {...}, delete: function() {...}} ) are separate. In those cases you can position two Metawidgets as siblings (or nested within each other), each pointing at different objects.
Finally for tables use a type of array. In JSON schema, you nest an items object that further nests a properties object, to describe the properties of the array items. See this example http://blog.kennardconsulting.com/2016/04/metawidget-and-angular-arrays.html (it's for Angular, but the JSON Schema is the same)

Filtering grid with QueryReadStore

I have a grid wich store is a QueryReadStore. It works fine, even the virtual scrolling.
The problem is the filter, when I filter, it generates a strange URL like this:
http://mydomain:8080/project=%3F&1=f&2=i&3=l&4=t&5=e&6=r&7=......
And I want to looks like this:
http://localhost:8080/project?filter={%22op%22:%22contains%22,%22data%22:[{%22op%22:%22string%22,%22data%22:%22username%22,%22isCol%22:true},{%22op%22:%22string%22,%22data%22:%22s%22,%22isCol%22:false}]}
Here is the code that generates the grid and the filter:
this.grid = new EnhancedGrid({
store: null,
structure: this.columns,
rowsPerPage: 20,
autoHeight: false,
plugins: {
filter: {
closeFilterbarButton: false,
isServerSide: true,
setupFilterQuery: dojo.hitch(this, function(commands, request){
if(commands.filter && commands.enable){
var gridStoreURL = this.grid.store.url;
if(gridStoreURL.indexOf("?") > -1) {
request.query = "&filter=" + JSON.stringify(commands.filter);
} else {
request.query = "?filter=" + JSON.stringify(commands.filter);
}
}else{
}
}),
ruleCount: 3,
itemsName: "logs",
disabledConditions: {anycolumn : this.disabledFilterAnyColumn}
}
}
}, this.idGridContainer);
I create the store with this function:
var store = dojox.data.QueryReadStore({
url : this.urlBase + agentId,
requestMethod:"get"
});
this.grid.setStore(store, null, null);
When I use JsonStore to create the store, the filter works fine, but with this doesn't.
Thank you in advance
You appear to be attempting to set the store query to a string, but IIRC QueryReadStore only ever expects the query to be passed as an object, as opposed to JsonRestStore which can accept it either way.
Try something like this instead, for starters, and see if it gets you further:
request.query = { filter: JSON.stringify(commands.filter) }

Insert in DB Issue, using OData and Kendo Grid

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

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

Dojo Tree : bridge from *unformatted* json to expected format

I am very new to Dojo (1.7), and I am very excited by the AMD loader and the global philosophy, then thought I have red some dozen of documentation and googled a lot and my brains starts to grill, I am still unable to understand and perform some things : I would like to display a dijit.Tree of any sort of JSON, yes like a JSON editor, because I use also persistent JSON files for storing few datas (not only for GET/.../ transmission) . Here are my expects :
sample JSON : {"infos":{"address":"my address","phone":"my
phone"},"insurance":{"forks":[14,53,123],"prices":[5,8,"3%"]}}
display the differents variables of any JSON : the root child is the
root json variable, children L1 are the root variables, etc...and upon the json variable type (String, Number, Object, Array) I will also display a corresponding icon
not to have to parse the whole json and format it in one big time, would like for exemple to display first the root node, then the well formated children trought a getChildren method for example, so it is done progressively on expando (like a lazy load). I have already made my own Trees classes with javascript, the more flexible way was I gave a dataRoot, a renderItem(dataItem, domItem) and a getChildren(dataItem) to the constructor so I could perform and return all I want, the Tree only performed the rendering only when needed, the Tree had no knowing about datas structure neither modify it, but I am not sure to understand well why the dijit.Tree needs a so restrictive way of build...
Here is my last try, it might totally not the right way, (maybe I have to subclass) but as far as I understand, I need to play with 3 classes (dojo store, tree model and tree widget), but firstly it seems the model can't get the root node, please check my different code comments. So please is there any patient person that can give me a simple example with some clear explanations (yeah I am a bit demanding), at least the list of the right necessary variables for constructor's options I need for start displaying a nice tree view of my json file, there's so much I'm totally lost, many thanks !
...
// before there is the AMD part that load the needed things
Xhr.get({ url:'data/file.json', handleAs:'json',
load: function(data){
console.log('xhr.loaded : ', data);// got my javascript object from the json string
var store = new ItemFileReadStore({// is it the right store I need ??
// or the Memory store ?
// assuming later I'll need to save the data changes
rootId : 'root',//
rootLabel : 'Archive',// useless ? isn't it the model responsability ?
data : {id:'root', items:[data]}// trying to give a root node well formatted
});
var model = new TreeStoreModel({
store : store,
getChildren : function(obj){
// firstly here it seems the root is not found
// I got a 'error loading root' error
// what is missing in my instanciations ??
// what is exactyly the type of the 1st arg : a store ?
console.log('getChildren : ', this.get(obj.id));
},
mayHaveChildren : function(){
console.log('mayHaveChildren ', arguments);
return true;
}
});
var tree = new Tree({
model: model
}, domId);
tree.startup();
}
});
My solution is based on dojo/store/Memory inspired by Connecting a Store to a Tree:
You can find live demo at http://egoworx.com/ or download complete source from dropbox.
Now code. First dojo/store/Memory:
var data = {"infos":{"address":"my address","phone":"my phone", "gift": false, "now": new Date()},"insurance":{"forks":[14,53,123],"prices":[5,8,"3%"]}};
var store = new Memory({
data: data,
mayHaveChildren: function(object) {
var type = this.getType(object);
return (type == "Object" || type == "Array");
},
getChildren: function(object, onComplete, onError) {
var item = this.getData(object);
var type = this.getType(object);
var children = [];
switch(type) {
case "Array":
children = item;
break;
case "Object":
for (i in item) {
children.push({label: i, data: item[i]});
}
break;
}
onComplete(children);
},
getRoot: function(onItem, onError) {
onItem(this.data);
},
getLabel: function(object) {
var label = object.label || object + "";
var type = this.getType(object);
switch(type) {
case "Number":
case "String":
case "Boolean":
case "Date":
var data = this.getData(object);
if (data != label) {
label += ": " + this.getData(object);
}
}
return label;
},
getData: function(object) {
if (object && (object.data || object.data === false) && object.label) {
return object.data;
}
return object;
},
getType: function(object) {
var item = this.getData(object);
if (lang.isObject(item)) {
if (lang.isArray(item)) return "Array";
if (lang.isFunction(item)) return "Function";
if (item instanceof Date) return "Date";
return "Object";
}
if (lang.isString(item)) return "String";
if (item === true || item === false) return "Boolean";
return "Number";
},
getIconClass: function(object, opened) {
return this.getType(object);
}
});
Please note I added a boolean and Date type to your data.
dijit/Tree based on this store:
var tree = new Tree({
model: store,
persist: false,
showRoot: false,
getIconClass: function(object, opened) {
if (lang.isFunction(this.model.getIconClass)) {
return this.model.getIconClass(object, opened);
}
return (!item || this.model.mayHaveChildren(item)) ? (opened ? "dijitFolderOpened" : "dijitFolderClosed") : "dijitLeaf";
}
}, "placeholder");
tree.startup();
And finally a stylesheet to display data type icons:
.dijitTreeIcon {
width: 16px;
height: 16px;
}
.Object {
background-image: url(http://dojotoolkit.org/api/css/icons/16x16/object.png);
}
.Array {
background-image: url(http://dojotoolkit.org/api/css/icons/16x16/array.png);
}
.Date {
background-image: url(http://dojotoolkit.org/api/css/icons/16x16/date.png);
}
.Boolean {
background-image: url(http://dojotoolkit.org/api/css/icons/16x16/boolean.png);
}
.String {
background-image: url(http://dojotoolkit.org/api/css/icons/16x16/string.png);
}
.Number {
background-image: url(http://dojotoolkit.org/api/css/icons/16x16/number.png);
}
I cannot access jsFiddle since I'm currently in China, but I'll put the code above there upon my return to Europe and post a link here.
Try somethign like that instead :
store = new dojo.data.ItemFileWriteStore({
url : "",
data: {
identifier: "id",
label : "label",
items : [{
id : "root",
label : "root",
type : "root",
children: [data]
}]
}
});
Also in general avoid overriding the tree functions, you might extend them, but becareful.
If you want to console.log, then rather connect to them...
ItemFileReadStore is a read-only store, so not the one you want for "saving modifications".
You can try the ItemFileWriteStore, or JsonRest, etc.