extjs store sometimes calling create instead of update - javascript

We have the following store in ExtJS 4.2:
Ext.define('Example.store.BasketDocuments', {
extend: 'Ext.data.Store',
model: 'Example.model.Document',
autoLoad: true,
autoSync: true,
sorters: [
{
property: 'doc_type',
direction: 'ASC'
}
],
proxy: {
type: 'rest',
url: baseUrl + 'document_basket',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json;charset=utf-8'
},
reader: {
type: 'json',
root: 'items'
},
writer: {
type: 'json'
},
actionMethods: {create: "POST", read: "GET", update: "PUT", destroy: "DELETE"}
}
});
It is attached to a grid with drag and drop functionality.
When we drag around 10 files (for 9 it works) to the grid which would immediately update the store, we get a server error, because we do not implement the POST function for URLs like
/api/document_basket/1964?_dc=1459498608890&{}
This is only for one entry.
For the others it would be
/api/document_basket?_dc=1459498608941&{}
which works.
Dragging only that single entry works.
So ExtJS is sending a POST request with an ID in the URL, which should be a PUT instead? Why is that?

I was able to fix this in my project.
Reason was that I was adding items to the store in a loop - so after each add of - let's say 14 files - a sync was done.
I discovered that there were 105 requests, which is just 1+2+3+4+5+6+7+8+9+10+11+12+13+14 so this caused a race condition.
Solution is to disable syncing before the loop:
onBeforeDropItem: function (node, data, overModel, dropPosition, dropHandlers, eOpts) {
dropHandlers.cancelDrop();
var store = Ext.getStore('BasketDocuments');
store.suspendAutoSync(); // new
if (node.id != 'documenttreepanel-body') {
Ext.Array.each(data.records, function (r, index) {
r = r.copy();
r.phantom = true;
r.data.id = null;
r.data.download_size = 1;
r.data.download_type = 1;
if (r.data.doc_type == 1) {
if (r.data.count == 0) {
Ext.create('Ext.window.MessageBox').show({
title: Ext.ux.Translate.get('Info'),
msg: Ext.ux.Translate.get('Ordner') + '<b>' + r.data.name + '</b>' + Ext.ux.Translate.get(' Is empty and cannot be added ') + '.',
buttons: Ext.Msg.OK,
modal: true
});
} else {
store.add(r);
}
} else {
store.add(r);
}
});
}
store.sync(); // new
store.resumeAutoSync(); // new

Related

Navigating between Kendo Pages by using ID and showing data

I have 2 views in my MVC project From the View1 I am taking an ID and passing it to View2. On view2, I already have KendoGrid and I have controller that reads all data for me and display in grid.
My question is how to get data from ID in View2? I copied my script code of View2 below
var crudServiceBaseUrl = "http://localhost:23355/",
dataSource = new kendo.data.DataSource({
transport: {
read: {
type: "GET",
url: crudServiceBaseUrl + "/api/SpecificationDetails",
dataType: "json",
cache: false
},
update: {
// update code goes here
},
},
destroy: {
// delete code goes here
},
create: {
// create code goes here
},
parameterMap: function (options, operation) {
console.log(operation + '-' + options.models);
if (operation === "create" && options.models) {
options.models[0].SpexHeaderId = 5;
var jsonstr = JSON.stringify(options.models[0])
console.log(jsonstr);
return jsonstr;
}
else if (operation === "update" && options.models) {
var jsonstr = JSON.stringify(options.models[0])
console.log(jsonstr);
return jsonstr;
}
else if (operation !== "read" && options.models) {
return { models: kendo.stringify(options.models) };
}
}
},
batch: true,
pageSize: 4,
schema: {
model: {
id: "SpecificationDetailId",
fields: {
SpecificationDetailId: { editable: false, type: "number" },
DescriptionTitle: "DescriptionTitle",
Description: "Description",
}
},
total: function (response) {
return response.total;
}
}
});
You are running jQuery 1.5.
Kendo UI requires a minimum of jQuery 1.7.1 (for Kendo UI 2011.3.1129). The current official version of Kendo UI (2017.2.504 (R2 2017)) requires jQuery 1.12.3.
Please refer to this chart for the specific version of jQuery that you require for your version of Kendo UI. You can grab a link to any version of jQuery from code.jquery.com.
If you are using legacy code, you'll additionally need to include jQuery Migrate.
Hope this helps! :)

Properly Initialize Client Side Model

My issue is very simple. I am using ASP Web API, Entity Framework, Angular, and Kendo UI. I have 2 classes, FREQUENCY and FREQ_TYPE_. Class FREQUENCY has a navigation property to class FREQ_TYPE. I have a kendo ui grid that loads 10 class FREQUENCY models. Each class FREQUENCY model has it's FREQ_TYPE data loaded properly. My problem is that when I create a new row in my kendo ui grid and try to save the row to the server, I get an error saying the navigation property FREQ_TYPE needs to be initialized. This is expected of course since kendo doesn't know how to auto=initialize my nav properties.
What is the best practice for giving my angular JS client the knowledge it needs to create a new class FREQ_TYPE so I can properly initialize class FREQUENCY and save it to the server? My models only exist as code-first entity models, so I can't just create a new model in my client side JS as it doesn't know about these models. Is there some framework that can generate local model classes from an EF database? Or do I just have to manually set all the json fields for my class FREQ_TYPE navigation property? Or is there an easier way for me to use Web API so that I can make a request to "figure out" what the model info is and create a client side JS model without needing to have a "local model"?
Here is the client side grid and datasource:
$(document).ready(function () {
var crudServiceBaseUrl = "http://localhost:29858/";
var NIICDDS = new kendo.data.DataSource({
transport: {
read: {
url: crudServiceBaseUrl + "api/NIICDFreq",
dataType: "json"
},
update: {
url: function (data) {
console.log("DATA TEST");
console.log(data);
return crudServiceBaseUrl + "api/NIICDFreq/";
},
// url: crudServiceBaseUrl + "api/VHFMasterLists",
dataType: "json",
data: function (data) {
console.log("returning data in update TEST");
console.log(data.models[0]);
return data.models[0];
},
type: "PUT",
contentType: "application/json; charset=utf-8",
},
destroy: {
url: crudServiceBaseUrl + "api/NIICDFreq",
dataType: "json"
},
create: {
url: crudServiceBaseUrl + "api/NIICDFreq",
dataType: "json",
type: "POST",
contentType: "application/json; charset=utf-8"
},
parameterMap: function (model, operation) {
if (operation !== "read" && model) {
return kendo.stringify(model);
} else {
return kendo.stringify(model) ;
}
}
},
batch: true,
pageSize: 20,
schema: {
data: function (data) { //specify the array that contains the data
console.log("DATA RETURN TEST");
console.log(data);
return data || [];
},
model: {
id: "Id",
fields: {
Id: { editable: false,
nullable: false,
type: "number"
},
Frequency: { type: "string" }
}
}
}
});
$("#NIICDFreqGrid").kendoGrid({
dataSource: NIICDDS,
columns: [
{ field: "Id", title: "Freq ID", format: "{0:c}", width: "120px" },
{ field: "Frequency", title: "Frequency Test", format: "{0:c}", width: "120px" },
{ command: ["edit", "destroy"], title: " ", width: "250px" }
],
toolbar: ["create"],
editable: "inline"
});
});
And here is the web api controller:
[ResponseType(typeof(FREQUENCY))]
public IHttpActionResult PostFREQUENCY(FREQUENCY testfreq)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
db.FREQUENCIES.Add(testfreq);
try
{
db.SaveChanges();
}
catch (DbUpdateException)
{
if (FREQUENCYExists(testfreq.Id))
{
return Conflict();
}
else
{
throw;
}
}
return CreatedAtRoute("DefaultApi", new { id = testfreq.Id }, testfreq);
}
The error is the last line:
iisexpress.exe Information: 0 : Request, Method=POST, Url=http://localhost:29858/api/NIICDFreq, Message='http://localhost:29858/api/NIICDFreq'
iisexpress.exe Information: 0 : Message='NIICDFreq', Operation=DefaultHttpControllerSelector.SelectController
iisexpress.exe Information: 0 : Message='CFETSWebAPI.Controllers.Frequency.NIICDFreqController', Operation=DefaultHttpControllerActivator.Create
iisexpress.exe Information: 0 : Message='CFETSWebAPI.Controllers.Frequency.NIICDFreqController', Operation=HttpControllerDescriptor.CreateController
iisexpress.exe Information: 0 : Message='Selected action 'PostFREQUENCY(FREQUENCY testfreq)'', Operation=ApiControllerActionSelector.SelectAction
iisexpress.exe Information: 0 : Message='Value read='DomainModelModule.FREQUENCY'', Operation=JsonMediaTypeFormatter.ReadFromStreamAsync
iisexpress.exe Information: 0 : Message='Parameter 'testfreq' bound to the value 'DomainModelModule.FREQUENCY'', Operation=FormatterParameterBinding.ExecuteBindingAsync
iisexpress.exe Information: 0 : Message='Model state is invalid.
testfreq.FREQ_POOL: The FREQ_POOL field is required.,testfreq.FREQ_TYPE: The FREQ_TYPE field is required.', Operation=HttpActionBinding.ExecuteBindingAsync
And of course testfreq has all null values.
Thank you for your help.
Since you shared no code, I can only make an assumption. However, I think you're confused with the error message. Neither Kendo or Angular are responsible. They do not "initialize" classes. You said yourself, the data is there on the client.
From what it sounds like to me, the data arrives at your controller action, and the compiler does not know how to initialize your class. Make sure your Class B has a constructor defined in your server-side code. Even an empty constructor will suffice, unless the members of the class need explicit initialization themselves.
public class B {
// constructor
public B() {
// initialize class members
}
}

Cannot refresh TreeStore in EXTjs

I'm trying to create a web-page in EXTJs that has two major components:
A Form (Ext.form.Panel)
A Panel (Ext.tree.Panel)
The form is supposed to get some values, which should populate tree in second panel. In the button handler of the first panel I have access to the updated JSON object, but I cannot figure out a way to refresh the TreeStore that will update the display in tree.Panel.
This is what I have so far :
Ext.define('TreeServiceData',{
config:{
data : ''
},print : function() {
console.log("Printing data: ")
console.log(this.data.children[0])
}
});
var dataObject = Ext.create('TreeServiceData');
dataObject.setData({'expanded':false,'children':[{'text':'Master','expanded':true,'leaf':false,'children':[{'text':'Child 1','expanded':true,'leaf':false,'children':[{'text':'Child 2','expanded':true,'leaf':false,'children':[{'text':'Child 3','expanded':false,'leaf':false}]}]}]}]})
Ext.define('TreeStoreData',{
extend: 'Ext.data.TreeStore',
model: 'TaskModel',
autoLoad: true,
autoSync: true,
proxy: {
type:'memory',
reader: {
type:'json'
}
},
root:dataObject.getData()
});
var treeStore = Ext.create('TreeStoreData');
Now I'm trying to update and display the value of this treestore on a button call which looks like this :
buttons:[
{
text:'Get CCP/Product',
handler:function (btn, evt) {
dataObject.print();
treeStore.removeAll();
dataObject.setData({'expanded':false,'children':[{'text':'Master11','expanded':true,'leaf':false,'children':[{'text':'Child 12','expanded':true,'leaf':false,'children':[{'text':'Child 23','expanded':true,'leaf':false,'children':[{'text':'Child 34','expanded':false,'leaf':false}]}]}]}]})
dataObject.print();
}
}
]
But on this button handler I'm always getting a "Uncaught TypeError: Cannot call method 'indexOf' of undefined " on treeStore.removeAll() method, where treestore is clearly defined in this context.
Question 1) What is the correct way to refresh a TreeStore ?
Answer 1)
Instead of:
treeStore.removeAll();
dataObject.setData( ... );
You should do:
dataObject.setData( ... ); // This won't affect the store
treeStore.setRootNode(dataObject.getData()); // Actually update the store
Note that changing dataObject's data won't affect the store automatically like you seem to think...
this code works for me (ExtJS 4.2.1)
Total tree panel nodes refresh example:
var responseDictObjects = $.ajax({
data: { Id: this.idDictionary },
dataType: "json",
type: "POST",
cache: false,
url: 'http://' + config.domain + '/' + 'api/Dictionaries/GetDictTreeData',
async: false
}).responseText;
responseDictObjects = jQuery.parseJSON(responseDictObjects);
while (this.storeDict.getRootNode().firstChild) {
this.storeDict.getRootNode().removeChild(this.storeDict.getRootNode().firstChild);
}
this.storeDict.getRootNode().appendChild(responseDictObjects.Data);
Replace this.storeDict with your store reference.
In my case:
JSON.stringify(responseDictObjects.Data)
returns
"[{"id":8,"text":"kkk","leaf":false,"expanded":true,"children":null},{"id":17,"text":"ttttt","leaf":false,"expanded":true,"children":null},{"id":22,"text":"gggg","leaf":false,"expanded":true,"children":null},{"id":23,"text":"qqq","leaf":false,"expanded":true,"children":null},{"id":24,"text":"fff","leaf":false,"expanded":true,"children":null},{"id":27,"text":"fff","leaf":false,"expanded":true,"children":null},{"id":28,"text":"ggggggggggggggggggg","leaf":false,"expanded":true,"children":null},{"id":31,"text":"ttttttttttt666666666","leaf":false,"expanded":true,"children":null},{"id":32,"text":"ffffffffffffffffffff","leaf":false,"expanded":true,"children":null},{"id":33,"text":"kkkkkkkkkkkkk","leaf":false,"expanded":true,"children":null},{"id":35,"text":"7777777777","leaf":false,"expanded":true,"children":null},{"id":36,"text":"999999999999","leaf":false,"expanded":true,"children":null},{"id":37,"text":"iii","leaf":false,"expanded":true,"children":null}]"
I found another bug with TreePanel, previosly removed nodes appears after executing appendChild. So i started to use jquery tree plugin (dynatree). All you need is to create empty panel. And after render a panel, embed tree, in my case:
Create empty panel:
that.treePanel = Ext.create('Ext.panel.Panel', {
title: 'Records',
width: 350,
height: 400
});
After rendering panel you can refresh nodes whenever you want:
var that = this;
var responseDictObjects = $.ajax({
data: { Id: this.idDictionary },
dataType: "json",
type: "POST",
cache: false,
url: 'http://' + config.domain + '/' + 'api/Dictionaries/GetDictTreeData',
async: false
}).responseText;
responseDictObjects = jQuery.parseJSON(responseDictObjects);
var el = this.treePanel.getId();
if (this.treeDict == null) {
this.treeDict = $('#' + el).dynatree({
onActivate: function (node) {
that.treeDict.lastSelectedId = node.data.index;
},
children: []
});
}
this.treeDict.dynatree("getRoot").removeChildren(true);
this.treeDict.dynatree("getRoot").addChild(responseDictObjects.Data);

diagnosing empty store load

I'm calling a web service with my Sencha Touch mobile app:
Ext.regModel('BaseResponse', {
idProperty: 'ResponseTime',
fields: [
{ name: 'ErrorMessage', type: 'string' },
{ name: 'ResponseTime', type: 'date', dateFormat: 'c' },
{ name: 'StatusCode', type: 'string' },
{ name: 'Success', type: 'string' }
]
});
var declineResult = new Ext.regStore('declineResult',
{
model: 'BaseResponse',
proxy : {
type : 'ajax',
dataType: "json",
url : App.BaseURL + '/SetJobResponse/' + options.jobId + '/' + STCID +'/' + options.OJSStatusID + '/' + device.uuid,
reader: new Ext.data.JsonReader ({
type: 'json'
})
},
listeners:
{
'load': function(store,records,successful)
{
alert(records.length);
//alert('response message:' + Ext.StoreMgr.get("declineResult").getAt(0).ErrorMessage);
},
'loadexception': function()
{
alert('There was a load exception');
}
}
});
Ext.StoreMgr.get("declineResult").load();
Here's the JSON returned by the URL if I just browse to it:
{"ErrorMessage":"You are not authorised","ResponseTime":"\/Date(1321447985287)\/","StatusCode":401,"Success":false}
However even though my load event shows Successful=true, records is empty (length of 0).
The exception event is not being fired.
How can I diagnose this further? I'm using Eclipse with Sencha Touch and Phonegap with an android emulator. Is there any way to see what's being returned to it?
I found that Sencha 1.x doesn't seem to be able to handle these responses:
404
an empty array of JSON data
a single object being returned instead of an array
What I ended up doing was using Ext.override to implement proper client-side responses to these server responses.
To get this working, you have to debug (use the Sencha debug libraries and place breakpoints there, using your JavaScript debugger) and see where your app crashes. You'll then find Ext.data.Reader in the callstack of your crash.
The next step is to override its member functions like extractData and readRecords to implement the proper functionality (like null pointer checks where necessary).
[edit] Relevant link:
http://docs.sencha.com/touch/1-1/source/Reader.html#Ext-data-Reader
I ended up getting around it by eliminating the need for a store:
Ext.regController('RequestDetailsC', {
userSettings: function() {
console.log('reqdetails controller called.');
},
declineRequest: function(options) {
console.log('decline called.');
var STCID = '8';
Ext.Ajax.request({
url: App.BaseURL + '/SetJobResponse/' + options.jobId + '/' + STCID +'/' + options.OJSStatusID + '/' + device.uuid,
method: 'GET',
success: function(result, request) {
var resultJson = Ext.decode(result.responseText);
alert(resultJson.ErrorMessage);
},
failure: function(result, request) {
Ext.Msg.alert('Error!', 'There was a problem while loading the data...');
}
});
}

How to display the searched data on the jgrid

this is related to my previous question about jqgrid. im doing now a search button that would search my inputed text from the server and display those data (if there is) in the jqgrid. Now, what i did is i create a global variable that stores the filters. Here's my javascript code for my searching and displaying:
filter = ''; //this is my global variable for storing filters
$('#btnsearchCode').click(function(){
var row_data = '';
var par = {
"SessionID": $.cookie("ID"),
"dataType": "data",
"filters":[{
"name":"code",
"comparison":"starts_with",
"value":$('#searchCode').val(),
}],
"recordLimit":50,
"recordOffset":0,
"rowDataAsObjects":false,
"queryRowCount":true,
"sort_descending_fields":"main_account_group_desc"
}
filter="[{'name':'main_account_group_code','comparison':'starts_with','value':$('#searchCode').val()}]";
$('#list1').setGridParam({
url:'json.php?path=' + encodeURI('data/view') + '&json=' + encodeURI(JSON.stringify(par)),
datatype: Settings.ajaxDataType,
});
$('#list1').trigger('reloadGrid');
$.ajax({
type: 'GET',
url: 'json.php?' + $.param({path:'data/view',json:JSON.stringify(par)}),
dataType: Settings.ajaxDataType,
success: function(data) {
if ('error' in data){
showMessage('ERROR: ' + data["error"]["msg"]);
}
else{
if ( (JSON.stringify(data.result.main.row)) <= 0){
alert('code not found');
}
else{
var root=[];
$.each(data['result']['main']['rowdata'], function(rowIndex, rowDataValue) {
var row = {};
$.each(rowDataValue, function(columnIndex, rowArrayValue) {
var fldName = data['result']['main']['metadata']['fields'][columnIndex].name;
row[fldName] = rowArrayValue;
});
root[rowIndex] = row;
row_data += JSON.stringify(root[rowIndex]) + '\r\n';
});
}
alert(row_data); //this alerts all the data that starts with the inputed text...
}
}
});
}
i observed that the code always enter this (i am planning this code to use with my other tables) so i put the filter here:
$.extend(jQuery.jgrid.defaults, {
datatype: 'json',
serializeGridData: function(postData) {
var jsonParams = {
'SessionID': $.cookie("ID"),
'dataType': 'data',
'filters': filter,
'recordLimit': postData.rows,
'recordOffset': postData.rows * (postData.page - 1),
'rowDataAsObjects': false,
'queryRowCount': true,
'sort_fields': postData.sidx
};
return 'json=' + JSON.stringify(jsonParams);
},
loadError: function(xhr, msg, e) {
showMessage('HTTP error: ' + JSON.stringify(msg) + '.');
},
});
now, my question is, why is it that that it displayed an error message "Server Error: Parameter 'dataType' is not specified"? I already declared dataType in my code like above but it seems that its not reading it. Is there anybody here who can help me in this on how to show the searched data on the grid?(a function is a good help)
I modified your code based on the information from both of your questions. As the result the code will be about the following:
var myGrid = $("#list1");
myGrid.jqGrid({
datatype: 'local',
url: 'json.php',
postData: {
path: 'data/view'
},
jsonReader: {
root: function(obj) {
var root = [], fields;
if (obj.hasOwnProperty('error')) {
alert(obj.error['class'] + ' error: ' + obj.error.msg);
} else {
fields = obj.result.main.metadata.fields;
$.each(obj.result.main.rowdata, function(rowIndex, rowDataValue) {
var row = {};
$.each(rowDataValue, function(columnIndex, rowArrayValue) {
row[fields[columnIndex].name] = rowArrayValue;
});
root.push(row);
});
}
return root;
},
page: "result.main.page",
total: "result.main.pageCount",
records: "result.main.rows",
repeatitems: false,
id: "0"
},
serializeGridData: function(postData) {
var filter = JSON.stringify([
{
name:'main_account_group_code',
comparison:'starts_with',
value:$('#searchCode').val()
}
]);
var jsonParams = {
SessionID: $.cookie("ID"),
dataType: 'data',
filters: filter,
recordLimit: postData.rows,
recordOffset: postData.rows * (postData.page - 1),
rowDataAsObjects: false,
queryRowCount: true,
sort_descending_fields:'main_account_group_desc',
sort_fields: postData.sidx
};
return $.extend({},postData,{json:JSON.stringify(jsonParams)});
},
loadError: function(xhr, msg, e) {
alert('HTTP error: ' + JSON.stringify(msg) + '.');
},
colNames:['Code', 'Description','Type'],
colModel:[
{name:'code'},
{name:'desc'},
{name:'type'}
],
rowNum:10,
viewrecords: true,
rowList:[10,50,100],
pager: '#tblDataPager1',
sortname: 'desc',
sortorder: 'desc',
loadonce:false,
height: 250,
caption: "Main Account"
});
$("#btnsearchCode").click(function() {
myGrid.setGridParam({datatype:'json',page:1}).trigger("reloadGrid");
});
You can see the code live here.
The code uses datatype:'local' at the beginning (at the 4th line), so you will have no requests to the server if the "Search" button is clicked. The serializeGridData the data from the postData parameter of serializeGridData will be combined with the postData parameter of jqGrid (the parameter "&path="+encodeURIComponent('data/view') will be appended). Additionally all standard jqGrid parameters will continue to be sent, and the new json parameter with your custom information will additionally be sent.
By the way, if you want rename some standard parameters used in the URL like the usage of recordLimit instead of rows you can use prmNames parameter in the form.
prmNames: { rows: "recordLimit" }

Categories

Resources