I have a JQGrid that updates via an ajax call to a web service.
It's all working fine, except from when I update the grid (and write it back to my database), the changes are not reflected in the grid.
I have read numurous posts with people reporting similar issues, but have tried on the suggestions to no avail.
loadonce is set to false, I reset my datatype to JSON and I have tried to destroy the grid before reloading it.
Here is my code thus far;
function LoadGrid2() {
//jgcontracts Grid
$.ajax({
type: "POST",
contentType: "application/json",
url: "../WebService1.asmx/getDataContacts",
dataType: "json",
success: function (data) {
data = data.d;
$("#jqcontacts").jqGrid({
datatype: "local",
colNames: ['Contact ID', 'Customer ID', 'First Name', 'Last Name', 'Email'],
colModel: [
{ name: 'contid', key: true, index: 'contid', width: 55, editable: true },
{
name: 'cust_name', index: 'cust_name', width: 80, align: "left", editable: true, edittype: "select",
editoptions: {
value: {}
}
},
{ name: 'first_name', index: 'first_name', width: 55, editable: true },
{ name: 'last_name', index: 'last_name', width: 55, editable: true },
{ name: 'email', index: 'email', width: 170, editable: true }
],
data: data,
caption: "Contacts",
viewrecords: true,
height: 200,
rowNum: 10,
pager: "#jqcontactsPager"
});
$('#jqcontacts').navGrid('#jqcontactsPager',
// the buttons to appear on the toolbar of the grid
{ edit: true, add: true, del: true, search: false, refresh: false, view: false, position: "left", cloneToTop: false },
// options for the Edit Dialog
{
url: "../WebService1.asmx/modifyDataContacts",
editData: {},
editCaption: "The Edit Dialog",
beforeShowForm: function (form) {
$('#contid', form).attr("disabled", true);
},
reloadAfterSubmit: true,
recreateForm: true,
checkOnUpdate: true,
checkOnSubmit: true,
closeAfterEdit: true,
errorTextFormat: function (data) {
return 'Error: ' + data.responseText
}
},
// options for the Add Dialog
{
url: "../WebService1.asmx/addDataContacts",
addData: {},
editCaption: "The Add Dialog",
beforeShowForm: function (form) {
$('#contid', form).attr("disabled", true);
},
closeAfterAdd: true,
recreateForm: true,
errorTextFormat: function (data) {
return 'Error: ' + data.responseText
}
},
// options for the Delete Dailog
{
url: "../WebService1.asmx/deleteDataContacts",
delData: {},
delCaption: "The Delete Dialog",
errorTextFormat: function (data) {
return 'Error: ' + data.responseText
}
});
},
error:
function (msg) {
alert(msg.status + " " + msg.statusText);
}
});
}
Here is my WebMethod
[WebMethod]
public object getDataContacts()
{
SqlConnection con = new SqlConnection(System.Configuration.ConfigurationManager.ConnectionStrings["Indigo2.Properties.Settings.Constr"].ConnectionString);
SqlCommand cmd = new SqlCommand();
cmd.CommandText = "SELECT [contid] ,cust.[cust_name] ,[first_name] ,[last_name] ,[email] FROM [Indigo].[dbo].[contacts] con LEFT JOIN [Indigo].[dbo].[customers] cust on con.custid = cust.custid";
cmd.CommandType = CommandType.Text;
cmd.Connection = con;
con.Open();
SqlDataAdapter da = new SqlDataAdapter(cmd);
con.Close();
DataSet ds = new DataSet();
da.Fill(ds);
object obj = new JavaScriptSerializer().DeserializeObject(Newtonsoft.Json.JsonConvert.SerializeObject(ds.Tables[0]));
return obj;
}
Any help greatly appreciated.
You do not need of this code.
afterSubmit: function () {
$("#jqcontacts").setGridParam({ datatype: 'json'}).trigger('reloadGrid');
return [true];
},
Like you do, you do two ajax calls. If you set editurl in grid parameters or url like you do the edited data is posted automatically to the server with ajax call instead that your datatype is local.
jqGrid looks for url(editurl) parameter and not for the datatype when posting edited data.
Remove the afterSubmit event and test. If the data is not saved you will need to see what you posted to the server and your server side code for saving data.
Guriddo jqGrid is server side independent javascript lib when we speak about saving, retrieving, sorting,... data from server side.
UPDATE
I see why is this caused.
Let me explain.
Question: How do you obtain your initial data?
Answer: You obtain your data with your own ajax call and then pass this data to a grid with datatype local.
Q: How do you update your data?
A: You update your data remotely to the server with separate call using a build in jqGrid functionality.
Problem: if data type is local and updating is server side the update does not reflect the local data in grid since reloading it, it reloads the current local data which is not affected from the update.
How to solve? You have more than one options.
Reconstruct your grid so that it obtain a data direct using the grid option url and jsonReader. Maybe you will need here to read the docs - i.e all your interactions with the data are server side.
In case you do not want to make server sorting, paging and etc. you can use the grid option loadonce to true with combination of url obtaining data from the servrer and jsonReader . In this case you will need to return all the data from the server (not in portion). If you do so then you can set datatype to json in beforeSubmit event so that when the grid reloads after update, then it will read the updated data from the server.
Do not change you current grid configuration, but in this case you will need to set the option reloadAfterSubmit to false in navigator and write additional for updating the local grid data.
I prefer you to use the option2.
I see that there is a small problem with the grid in this situationand we will try to fix it in the future release.
Related
This is my store MyStore.js
Ext.define('myProject.store.MyStore', {
config:{
storeId: 'MyStore',
autoLoad: false,
autoSync: false,
allowSingle: true,
clearOnPageLoad: true,
model: 'abc.model.MyStoreModel',
proxy: {
type: 'rest',
actionMethods: {create: 'POST', read: 'GET', update: 'POST', destroy: 'POST'},
url:'/services/rest/MyService/myService',
reader: {
type: 'json',
rootProperty:MyServiceView.elements.collection',
successProperty : 'success'
},
writer:
{
type: 'json',
root: 'MyServiceDataView',
nameProperty: 'mapping',
expandData : true
},
listeners: {
exception: function(proxy,response,operation){
}
}
}
}
});
This is my Model
Ext.define( 'myProject.model.MyStoreModel', {
extend: 'Ext.data.Model',
config:{
idProperty: 'keyStr',
fields:[
{
name: 'keyStr',
type: 'string',
},
{
name: 'sId',
type: 'int'
},
{
name: 'dCode',
type: 'string'
},
{
name: 'sNumber',
type: 'int'
}
]
},
});
Inside my Controller.js, I have this method
syncMyStore: function()
{
var deferred = Q.defer();
var successfulSync= 'false';
var me = this;
var myStore = Ext.getStore('MyStore');
if(this.isSyncRequires(myStore)) //assume this is always true
{
myStore.sync({
success: function () {
successfulSync = 'true';
deferred.fulfill();
}
});
}
else
{
deferred.fulfill();
}
return successfulSync;
},
Suppose I have 5 records in my store i.e record0, record2 ... record4.
For each records, it is calling the Rest Service. So total 5 Rest calls
Requirement 1: Instead of using success property, I want to perform some actions on the basis of status code.
i.e if status code is 200, then consider it success.
Requirement 2: After each rest call, I want to remove record/mark dirty as false on the basis of response status (200) for that particular record.
Means, suppose for record1 and record2 if status code is 200, then I want to remove/mark dirty=false for record 1 and record2 only.
I will be really thankful for you if you help me out with this.
Assuming that record0, record1, record3 .. have some unique way of identification. Make an ajax call for each record(REST api call ) then if the response is 200, control will hit "success" function. Then if the response has data to identify records select that record and change its dirty flag to false. In the failure function do the same and change dirty flag to true.
There are two things you need to conform to make better answer
Will you be using records as payload for your rest api call ?
Will the response have indication record, i.e i'll have guid related to
record ?
I wanna save a model by sending data using post method to my web server,
I wrote these codes and noticed that sencha is sending data using the GET method.
how can I send the data using POST method?
my model code:
Ext.define('MyApp.model.emp.Roles', {
extend: 'MyApp.model.Base',
fields: [
{
type: 'int',
name: 'id'
},
{
type: 'string',
name: 'title'
},
],
proxy: {
type: 'ajax',
url : 'http://myweb.test/json/fa/myweb/managerole.jsp',
idParam: 'id',
extraParams: {
'action':'btnSave_Click'
},
method:'POST',
actionMethods: {
create : 'POST',
read : 'POST',
update : 'POST',
destroy: 'POST'
},
}
});
save calling code:
{
xtype: 'button',
text: 'ذخیره',
handler:function()
{
var user = Ext.create('MyApp.model.emp.Roles', {title: 'A Role From Sencha'});
user.set('title','hi');
user.set('id',-1);
user.save({
params: user.getData(),
callback: function (records, operation) {
Ext.Msg.alert('User Save', operation.getResponse().responseText);
},
methodName:'POST',
method:'POST',
});
}
}
If you are using model then you need to use only actionMethods.
actionMethods Mapping of action name to HTTP request method. In the basic AjaxProxy these are set to 'GET' for 'read' actions and 'POST' for 'create', 'update' and 'destroy' actions. Defaults to:
{
create: 'POST',
read: 'GET',
update: 'POST',
destroy: 'POST'
}
In this FIDDLE, I have created a demo using model and button. I hope this will help/guide your to achieve your requirement.
*NOTE I have used only local url. I don't have any live url. You can see in network request data is sending in url.
CODE SNIPPET
Ext.application({
name: 'Fiddle',
launch: function () {
Ext.define('MyModel', {
extend: 'Ext.data.Model',
fields: ['version', 'code', 'framework', 'frameworkVersion', 'fiddleid', 'inspector', 'session'],
proxy: {
type: 'ajax',
url: 'local', //I am providing local url in your case you can provide your rest webservice url
useDefaultXhrHeader: false,
actionMethods: {
create: 'POST', //When you want to save/create new record
read: 'GET', //When you want to get data from server side
update: 'PUT', //When you want to update the record
destroy: 'DELETE' //When you want to delete the record
},
paramAsJson: true // if You want to encode the data from clint side then it should be true otherwise false
}
});
Ext.create({
fullscreen: true,
renderTo: Ext.getBody(),
xtype: 'panel',
title: 'sending data using POST method in sencha extjs rest webservice',
padding: 10,
items: [{
xtype: 'button',
text: 'Send Data',
margin: 15,
style: {
background: '#ccc'
},
height: 50,
width: '100%',
handler: function () {
var MyModel = Ext.create('MyModel', {
version: "2",
code: '',
framework: "291",
frameworkVersion: "",
fiddleid: "",
inspector: "",
session: ''
});
MyModel.save({
success: function (records, operation) {
//When data will save on sever side then response will come in success
Ext.Msg.alert('User Save', 'Data saved');
},
failure: function (records, operation) {
//If some error occure on server side the reponse will come in failure function
Ext.Msg.alert(`Error ${operation.error.status}`, operation.error.statusText);
}
});
}
}]
});
}
});
I am using jqgrid version 4.5.2. I have a role list combobox outside of the grid and on change of the options I need to reload the grid. Below is the defination of my grid.
var finalUrl='';
var queueStatus=jQuery('#queueStatus option:selected').val();
finalUrl= "http://localhost:8080/ui/paw/loadworkflowqueuedata.raws?selRole="+ selectedRole+"&"+queueStatus;
var queueStatus=jQuery('#queueStatus option:selected').val();
finalUrl= "http://localhost:8080/ui/paw/loadworkflowqueuedata.raws?timezone="+&selRole="+ selectedRole+"&"+queueStatus;
jq("#grid").jqGrid('GridUnload');
jq("#grid").jqGrid({
url:finalUrl,
ajaxGridOptions: {cache: false},//added the option to always reload the grid and not to cache the result.
datatype: 'json',
mtype: 'GET',
colNames:[ 'Requestor Name'],
colModel:[
{name:'requestor',index:'requestor',sortable: true, width:100,editable:false, editrules:{required:true}, editoptions:{size:10}}
],
postData: {
},
height: 'auto',
autowidth: true,
rownumbers: true,
pager: '#pager',
viewrecords: true,
sortorder: "asc",
emptyrecords: "Empty records",
loadonce: true,
rowNum:20,
ignoreCase: true,
prmNames: {
nd: null
},
loadComplete: function() {
},
jsonReader : {
root: "rows",
repeatitems: false,
page:"page",
total: "total",
records: "records",
cell: "cell",
id: "id"
}
});
jQuery("#grid").jqGrid('navGrid','#pager',{edit:false,add:false,del:false,search: false, refresh:true})
.navButtonAdd('#pager',{caption:"Export All",buttonicon:"ui-icon-document",onClickButton: function(){window.open(excelUrl,'_self');},position:"last"});
jQuery("#grid").jqGrid('filterToolbar', { stringResult: true, searchOnEnter: false, defaultSearch: "cn",ignoreCase: true });
The grid above works fine(refreshes properly on change of rolelist combobox) only if I keep the below statement.
jq("#grid").jqGrid('GridUnload');
If I remove the above statement and reload the page than for the first time grid loads properly but after that If I change the option in my role list combo box it is not able to refresh the grid data neither it is throwing any error.
May I know why I need to unload the grid for refresh? Isn't there a way through which I can refresh the grid with out unloading the grid? Did I miss any options in grid defination that is the reason why grid reload is not working?Please do let me know if need more details for the above question for the solution.
You use loadonce: true options which is very helpful to load all the server data to the client side at once and then working with the data on the client side (paging, filtering and so on) without any communication with the server. To do this jqGrid changes datatype parameter of the grid to "local" after the first loading of data. So you need to reset the value of datatype parameter back to "json" before trigger reloadGrid event.
The corresponding code will be like
// create the initial grid
jQuery("#grid").jqGrid({
...
url: finalUrl,
datatype: 'json',
loadonce: true,
...
});
jQuery('#queueStatus').change(function () {
jQuery("#grid").jqGrid("setGridParam", {
datatype: "json",
url: "basePartOfUrl?" + jQuery.param({
timezone: "blabla",
selRole: jQuery('#queueStatus').val();
})
}).trigger("reloadGrid");
});
I use above jQuery.param instead of direct construction of parameters in the string to make the code more correct. Alternatively one should use encodeURIComponent to construct the parameter values appended to URL. It's clear that one can use queueStatus directly if it has no space, no special characters and so on, but the usage of encodeURIComponent or jQuery.param is still recommended and make the code working independent from the value of the string parameter.
Please note that at this time I am new to ASP.NET MVC, JavaScript and Kendo. The Kendo grid paging works for the first 500 records returned, but won't allow paging to download more data from the server past 500. Here is my controller:
public ActionResult ExecuteTestRule(string ruleId, string ruleSql, string uwi, List<PdsMatchRuleParam> parameters = null)
{
if (Request.Url != null)
{
var query = PaginationQuery.Parse(Request.QueryString);
var upperLimit = query.FromUpper;
var lowerLimit = query.FromLower;
var dataSource = new MatchDataSource();
List<DataAccess.DbParameter> dbParameters = null;
var generatedSql = dataSource.GenerateQuerySql(ruleId, ruleSql, uwi, parameters, out dbParameters);
var results = dataSource.ExecuteTestRule(ruleId, generatedSql, dbParameters, upperLimit, lowerLimit).Select(u => new { UWI = u });
var response = new Dictionary<string, object>();
response["result"] = results;
response["rowCount"] = MatchDataSource.GetRowCount(generatedSql, dbParameters);
return Json(response, JsonRequestBehavior.AllowGet);
}
return null;
}
Here are the total number of rows available "rowCount" in the controller:
MatchDataSource.GetRowCount(generatedSql, dbParameters)
91637
Here is the Request.QueryString in the controller:
Request.QueryString
{}
[System.Web.HttpValueCollection]: {}
base {System.Collections.Specialized.NameObjectCollectionBase}: {}
AllKeys: {string[0]}
Pressing this button has no effect:
Here is my JavaScript code:
function bindTestRuleResults() {
PageState.Selected.Old.TestRuleResult = PageState.Selected.TestRuleResult;
var dataSource = new kendo.data.DataSource({
pageSize: 500,
data: PageState.Selected.TestRuleResult,
serverPaging: true
});
Grids.TestRuleResultsGrid.setDataSource(dataSource);
PageState.Selected.TestRuleResult = null;
}
function initTestRuleResultsGrid() {
$(IDS.testRuleResultsGrid).kendoGrid({
autoBind: true,
filterable: false,
navigatable: true,
pageable: {
refresh: true,
pageSizes: [10, 50, 100, 500],
buttonCount: 5
},
scrollable: true,
selectable: true,
serverFiltering: false,
serverPaging: true,
serverSorting: true,
sortable: false,
columns: [
{ field: "UWI", title: "UWI", width: "100%", attributes: { tabindex: "1" } }
],
change: function() {
var selectedDataItem = this.dataItem(this.select());
if (PageState.Selected.TestRuleResult !== selectedDataItem.TestRuleResult) {
PageState.Selected.TestRuleResult = selectedDataItem.TestRuleResult;
testRuleResultsSelectionChanged();
}
},
editable: false
});
// Add vertical scroll bars
$(IDS.testRuleResultsGrid + " .k-grid-content").css({
"overflow-y": "scroll"
});
Grids.TestRuleResultsGrid = $(IDS.testRuleResultsGrid).data('kendoGrid');
}
function execTestRule(uwi) {
$.ajax({
type: 'POST',
url: "ExecuteTestRule",
contentType: 'application/json; charset=utf-8',
data: JSON.stringify({
ruleId: PageState.Selected.RuleId,
ruleSql: PageState.SqlEditor.RuleSql.getValue(),
uwi: "'" + uwi + "'",
parameters: PageState.RuleParameters
}),
schema: {
errors: function(response) {
return response.error;
},
data: function(response) {
return response.result;
},
total: function(response) {
return response.rowCount;
}
},
success: function(matchedUwiList) {
PageState.TestRuleResult = matchedUwiList.result;
var dataSource = new kendo.data.DataSource({
pageSize: 500,
data: matchedUwiList.result,
serverPaging: true
});
Grids.TestRuleResultsGrid.setDataSource(dataSource);
PageState.Selected.ChildUwi = null;
updateButtonStates();
},
error: function(e) {
var errorObject = JSON.parse(e.xhr.responseText);
var errorMessage = errorObject.Message;
//clear old error message
Grids.TestRuleResultsGrid.clearErrorMessage("error-message");
// add new error message
Grids.TestRuleResultsGrid.addErrorMessage("error-message", errorMessage);
}
});
}
It clearly has serverPaging = true in the data source. What am I missing? Do I need to somehow make pageSize dynamic in my JavaScript code? TIA.
UPDATE:
Thank you for the feedback, #Brett. This is how I've simplified the code as you suggested. How do I remove the success: function outside of the ajax part?
function execTestRule(uwi) {
$.ajax({
type: 'POST',
url: "ExecuteTestRule",
contentType: 'application/json; charset=utf-8',
data: JSON.stringify({
ruleId: PageState.Selected.RuleId,
ruleSql: PageState.SqlEditor.RuleSql.getValue(),
uwi: "'" + uwi + "'",
parameters: PageState.RuleParameters
}),
success: function(matchedUwiList) {
PageState.TestRuleResult = matchedUwiList.result;
var dataSource = new kendo.data.DataSource({
schema: {
data: 'results',
total: 'rowCount'
},
pageSize: 500,
serverPaging: true
});
Grids.TestRuleResultsGrid.setDataSource(dataSource);
PageState.Selected.ChildUwi = null;
updateButtonStates();
}
});
}
When the execTestRule function is run, this is the error I'm getting:
You code is confusing to me, but I do see one particular problem. You are not telling the Kendo UI DataSource where your data and row count properties are in the returned object from your controller.
In your controller, you specify that the data is located in the response["results"] property, while the row count is in the response["rowCount"] property. Therefore, your returned object looks like this:
{
results: [...],
rowCount: 91637
}
The Kendo UI DataSource object's schema, by default, expects data to be located in a "data" property and the row count (a.k.a. number of items in data) to be located in the "total" property. Since your object does not conform to convention, you need to tell the data source that your properties are named differently.
var dataSource = new kendo.data.DataSource({
schema: {
data: 'results',
total: 'rowCount'
},
pageSize: 500,
serverPaging: true
});
So, you might say you are doing that already, but look where you defined it. You defined it on the $.ajax() call. That is not correct. The $.ajax() function does not care about schema. The Kendo UI DataSource does.
Kendo UI DataSource API reference
jQuery $.ajax API reference
As #Brett explained, resolved the problem by taking the schema out of the $.ajax() call. Also, started using a transport in the dataSource:
$.widget("myViewGrid", {
// ...
var dataSource = new kendo.data.DataSource({
transport: {
read: {
url: "{0}/ViewDataById".format(options.controllerUri),
type: "get",
dataType: "json",
data: {
viewId: view.viewId,
Id: options.Id
}
}
},
schema: {
data: "mydata",
total: "total",
model:dataSourceModel
},
pageSize: view.pageSize,
serverPaging: true,
serverSorting: true,
serverFiltering: true
});
// ...
});
I work with jqGrid on my project and I have a problem that can not seem to solve even after reading the forums on the same subject.
I would reload the grid when a user clicks a button after selecting a date. I want the data recharge correspodent to choose the date.
I use the following code:
var loadfacture = false;
$('#btn-facture').click(function(){
if(!loadfacture){
$("#factures").jqGrid({
url:'loadfactureencours.json',
datatype: "json",
autowidth: true,
height:250,
colNames:['#','Numero','Date', 'Client','Serveur','Prix Total','Avance','Regler','Notes'],
colModel:[
{name:'idfac',index:'idfac', width:45,align:"center"},
{name:'numfac',index:'numfac', width:80,align:"center"},
{name:'datefac',index:'datefac', width:150,align:"center"},
{name:'client',index:'client', width:145,align:"center"},
{name:'utilisateur',index:'utilisateur', width:125,align:"center"},
{name:'montant',index:'montant', width:70,align:"center"},
{name:'avance',index:'avance', width:60,align:"center"},
{name:'regler',index:'regler', width:50,align:"center"},
{name:'description',index:'description', width:150, sortable:false}
],
rowNum:10,
rowTotal: 2000,
rowList : [10,20,30,40,50,60],
loadonce:true,
mtype: "GET",
postData: {
day: function () {
return $('#daySelect').val();
}
},
rownumbers: false,
rownumWidth: 40,
gridview: true,
pager: '#paging',
sortname: 'idfac',
viewrecords: true,
sortorder: "desc",
caption: "",
ondblClickRow: function(rowid,iRow,iCol,e){
var rowData = jQuery(this).getRowData(rowid);
console.log(rowData);
window.location = "ecrancommandebar.html?num="+rowData.numfac;
}
});
$("#factures").jqGrid('navGrid','#paging',{del:false,add:false,edit:false});
loadfacture = true;
}else{
var grid = $("#factures");
grid.trigger("reloadGrid",[{current:true}]);
}
});
I built this piece of code by reading the forum on the same subject but the grid load the first time then when I click on the button after changing the date nothing happens.
What is wrong with you? Thank you for your feedback.
You use loadonce:true option which changes datatype from initial "json" to "local" after the first loading of the data from the server. It allows to support local sorting, paging and filtering of data. So you should understand that grid.trigger("reloadGrid",[{current:true}]); will just reload the data from previously saved previous response. So no reloading from the server will take place till you reset the value of datatype parameter to "json". In other words you should rewrite else part to about the following
...
} else {
$("#factures").jqGrid("setGridParam", {datatype: "json"})
.trigger("reloadGrid", [{current: true, page: 1}]);
}
You can try with this:
}else{
var grid = $("#factures");
$.ajax({
url: "loadfactureencours.json",
dataType: "json",
success: function(data){
grid.trigger("reloadGrid",[{current:true}]);
},
error: function(){}
});
}