jqGrid – 新行的唯一ID

我试图将一个唯一的ID设置为我添加到我的jqGrid的新行。 我使用免费的jqGrid 4.11.0,服务器端的java rest服务和数据库的MS SQL。

inlineEditing: { ajaxSaveOptions: { contentType: "application/json" }, serializeSaveData: function (postData) { var idArray = grid.getCol('id'); var count = 0; var k = 1; while(postData.id.search('jqg') != '-1'){ for(var i =0 ; i<idArray.length;i++){ if(k == idArray[i]){ count++; } if(count == 0){ postData.id = k ; break; } } k++; } return JSON.stringify(postData); } } 

我注意到jqGrid给出的默认ID是jqg +一个数字。 有关如何制作当前未在任何数据库记录中使用的唯一ID的任何建议? 我应该从服务器端执行此操作还是可以在jqGrid代码中执行此操作?

在晚上的某个时刻,这段代码有点工作,并继续为所有新行提供id 7 [即使它应该继续增加]。 经过一些更改[无法记住更改]后,每次单击“保存”将新行提交到服务器时,页面会冻结并且chrome建议我应该终止该过程。

请让我知道如果我应该添加更多信息。

编辑1:

 $(function () { var grid = $('#grid'), checkboxEditOptionEvents = [ {type : 'click', data: {'Yes': 'Yes'}, fn: function (e, id) { var checkboxCol = grid.getCol('sefDepartament'), ids = grid.jqGrid('getDataIDs'), numberOfCheckedBoxes = [], k; for (k = 0; k < ids.length; k++) { if(checkboxCol[k] == 'Yes'){ numberOfCheckedBoxes.push(checkboxCol[k]); if (numberOfCheckedBoxes.length == 1){ alert('Please deselect the other checked box first'); $(this).prop("checked",false); numberOfCheckedBoxes = 0; } } } }}]; var experienceFunction = function(cellvalue, options ,rowObject){ var joinYearVar = rowObject.joinYear, YY = joinYearVar.slice(0,4), MM = joinYearVar.slice(5,7), DD= joinYearVar.slice(8,11); return moment("\"" +YY+MM+DD+"\"" , "YYYYMMDD").fromNow(); }; var checkboxFormatFunc = function(cellvalue, options ,rowObject){ if(cellvalue == 'Yes'){ return 'Yes'; } return 'No'; }; var afterSaveFunction = function(id){ var prenumeVar = grid.getCell(id, 'prenume').trim(), numeVar = grid.getCell(id,'nume').trim(), usernameVar = numeVar +'.'+ prenumeVar, emailVar = usernameVar + '@test.com'; usernameVar =usernameVar.replace(/\s/g , '').trim(); emailVar = emailVar.replace(/\s/g , ''); grid.setCell(id, 'username', usernameVar); grid.setCell(id, 'email', emailVar); }; var colModelSettings = [ {name:'id', label:'id',key: true,hidden: true, width:10,sorttype:'number',editable: false}, {name:'nume',label:'Nume',width:90, align: 'center',editable:true,searchoptions: {sopt: ['eq','bw','ew','cn']}, editrules:{required:true}, editoptions: {defaultValue: ' '},formatter: 'text'}, {name:'prenume',label:'Prenume',width:100,editable:true,searchoptions: {sopt: ['eq','bw','ew','cn']},align: 'center',editrules:{required:true},editoptions: {defaultValue: ' '},formatter: 'text'}, {name:'username',label:'Username',searchoptions: {sopt: ['eq','bw','ew','cn']},width:125,align: 'center' }, {name:'email',label:'Email',width:135,searchoptions: {sopt: ['eq','bw','ew','cn']},align: 'center'}, {name:'sefDepartament',label:'Sef Departament',width:90,editable:true,align: 'center', stype:"select", searchoptions:{sopt: ['eq','ne'],value: "Yes:Yes;No:No"},formatter: checkboxFormatFunc,edittype:'checkbox',editoptions: { dataEvents: checkboxEditOptionEvents,value:'Yes:No', defaultValue: 'No' }}, {name:'position',label:'Position',editable:true,stype: 'select',formatter: 'select',searchoptions: {sopt: ['eq','ne'],value: ' : ;position 1:position 1;position 2:position 2;position 3:position 3;position 4:position 4;position 5:position 5'}, align: 'center',edittype:'select',editoptions:{defaultvalue: 'P0: ',value: ' : ;position 1:position 1;position 2:position 2;position 3:position 3;position 4:position 4;position 5:position 5'},width: 75}, {name:'joinYear',label:'Join Year',formatter:'date', formatoptions: {newformat:'dm-Y'}, datefmt: 'dd-mm-yyyy', editable:true,searchtype: 'datepicker',align: 'center',width: 70, searchoptions:{dateFormat:'dd-mm-yy',dataInit: function (elem){ $(elem).datepicker({ showButtonPanel: true, dateFormat: 'yy-mm-dd'});},sopt: ['eq','ne']}, editoptions:{size:20,defaultValue: ' ',dataInit: function (elem) { $(elem).datepicker({ showButtonPanel: true, dateFormat: 'dd-mm-yy'}); }}}, {name:'experience', label:'Experience', formatter: experienceFunction, searchoptions:{sopt: ['eq','bw','ew','cn']}, editable:'hidden', editoptions:{defaultValue: ' '},align: 'center',width: 60}, {name:'actiuni',label: 'Actiuni',formatter: 'actions', formatoptions: {afterSave:afterSaveFunction},editable: false,sortable: false,search: false,width: 20 } ]; grid.jqGrid({ pager: '#pager', url: "/RestWithDatabaseConnection/rest/fetchData", editurl:'/RestWithDatabaseConnection/rest/update', datatype: "json", height: 250, viewrecords: true, scrollOffset:0, sortorder: 'asc', caption:'Employee List' , autowidth: true, colModel: colModelSettings, beforeSelectRow : function(id){ var idsArray = grid.jqGrid('getDataIDs'); var i; for(i=0;i<idsArray.length;i++){ if($('#'+idsArray[i]).is('[editable="1"]') ){ grid.editRow(idsArray[i],true); return false; } } return true; }, inlineEditing: { ajaxSaveOptions: { contentType: "application/json" }, serializeSaveData: function (postData) { var idArray = grid.getCol('id'); var count = 0; var k = 1; while(postData.id.search('jqg') != '-1'){ for(var i =0 ; i<idArray.length;i++){ if(k == idArray[i]){ count++; } if(count == 0){ postData.id = k ; break; } } k++; } return JSON.stringify(postData); } } }); grid.jqGrid('navGrid', '#pager', {edit:false, add:false, delete:true, save:false, cancel:false, search:true, searchtext: 'Search', refresh:true}, {},{},{ url: '/RestWithDatabaseConnection/rest/delete', mtype: 'DELETE', reloadAfterSubmit: true, ajaxDelOptions: { contentType: "application/json", }, serializeDelData: function(postdata) { return JSON.stringify(postdata); }},{},{},{},{} ); grid.jqGrid('inlineNav','#pager', { edit:true, edittext: 'Edit', save:true, savetext: 'Save', add:true, cancel: true, canceltext: 'Cancel', cancelicon: 'ui-icon-cancel', addicon:'ui-icon-plus', addtext: 'Add', addedrow: 'last', addParams: { position: 'last', addRowParams: { aftersavefunc : afterSaveFunction, keys: true, } }, editParams:{ url: '/RestWithDatabaseConnection/rest/update', mtype : "POST", keys: true, aftersavefunc : afterSaveFunction, } })}) 

Edit2 – 服务器响应fetchData:

 [{"id":"3","nume":"Aladin","prenume":"Zoro","username":"Aladin.Zoro","email":"Aladin.Zoro@test.com","sefDepartament":"Yes","position":"position 4","joinYear":"2015-11-08","experience":"2 months"}, {"id":"2","nume":"Harap","prenume":"Alb","username":"Harap.Alb","email":"Harap.Alb@test.com","sefDepartament":"No","position":"position 1","joinYear":"2016-01-03","experience":"9 days "}, {"id":"4","nume":"Don","prenume":"Homa","username":"Don.Homa","email":"Don.Homa@test.com","sefDepartament":"No","position":"position 4","joinYear":"2015-09-06","experience":"4 months"}, {"id":"5","nume":"Dorel","prenume":"Gigel","username":"Dorel.Gigel","email":"Dorel.Gigel@test.com","sefDepartament":"No","position":"position 4","joinYear":"2016-01-10","experience":"2 days"}, {"id":"1","nume":"Ivan","prenume":"Stefan","username":"Ivan.Stefan","email":"Ivan.Stefan@test.com","sefDepartament":"No","position":"position 2","joinYear":"2016-01-10","experience":"2 days"}] 

以下是一些建议,以解决您的主要问题,并改进您发布的JavaScript代码。

首先,本地编辑方案需要在本地生成新的rowid。 如果将数据保存在数据库的后端,则应该在服务器上生成新的rowid。 典型的实现包括在每个表中将PRIMARY KEY定义为int IDENTITY 。 它使id独特且固定。 删除某行并创建新行永远不会被解释为编辑旧行,因为新行将始终获得新的id,这在以前从未使用过(在表中)。

为了利用服务器端生成的ID,我们有两个主要选择:

  1. 每次添加行操作后重新加载网格。
  2. 在编辑时扩展与服务器的通信,以便服务器将在数据库表中生成的新id返回给jqGrid。 在服务器上成功创建行之后,可以使用aftersavefunc回调(仅用于添加新行)来更新rowid。 RESTful服务的许多标准实现都在Add或Edit上返回完整行数据,包括id。 可以使用aftersavefunc回调中的数据并使用类似$("#" + rowid).attr("id", newRowid); 更新新行。 它在一些额外的列中保存了id(就像你使用隐藏的id列),然后还应该使用setCell方法来更新单元格。

第一个选择是最简单的,我建议你首先实现它。 只有重新加载网格不能满足用户一个接一个地添加许多行的用户,那么你应该多写一些代码并实现第二个场景。

您当前的代码使用inlineNav进行添加和编辑操作,使用内联编辑实现,使用navGrid进行删除操作,使用表单编辑实现。 表单编辑(包括Delete)默认使用reloadAfterSubmit: true选项。 这意味着在删除每一行后,网格将从服务器重新加载(来自url: "/RestWithDatabaseConnection/rest/fetchData" )。 您可以通过将afterSaveFunction替换为以下内容来解决您的主要问题:

 var afterSaveFunction = function () { $(this).trigger("reloadGrid", [{current: true, fromServer: true}]); }; 

重新加载后保持当前选择的当前选项和fromServer: true选项只有在你另外使用loadonce: true选项的情况下才有意义。 您可以使用reloadGridOptions: {fromServer: true}选项强制在点击导航栏的Refresh / Reload按钮时从服务器重新加载数据。 如果您没有需要在网格中显示的数据(例如,少于1000行),则建议使用此类行为。

您可以使用一些更常见的建议来改进代码:

您可以考虑使用height: "auto"而不是height: 250并通过指定rowNum值来管理网格的最大高度。 在这种情况下,将不需要选项scrollOffset: 0

从服务器返回的数据格式看起来如此,您没有实现服务器端分页,排序和过滤 。 您应该使用loadonce: trueforceClientSorting: true选项。 loadonce: true通知jqGrid在本地data参数中本地保存从服务器返回的所有 data 。 您可以随时使用$('#grid').jqGrid("getGridParam", "data")访问数组。 rowNum的值(默认值为20)将用于本地分页。 sortnamesortorder将用于本地排序。 您将使用搜索对话框(由navGrid添加)或filter工具栏(由filterToolbar添加)进行本地搜索/过滤。 它简化了服务器代码,从用户的角度提高了网格性能,并简化了服务器和客户端之间的接口。 您可以在服务器上使用经典的RESTful接口,而无需任何扩展。

另一句话:我建议你删除不需要的隐藏id列( name:'id', label:'id', key: true, hidden: true, ... )。 有关rowid的信息将保存在行的id属性(

元素)中,并且不需要在每行的隐藏

元素中保存重复信息。

您的代码还有许多其他部分,可以进行改进。 例如,您在服务器端使用的DELETE操作似乎很奇怪。 您使用mtype: 'DELETE' ,但是您将请求正文中已删除行的ID发送到服务器,而不是将其附加到URL。 对应标准,HTTP DELETE应该不包含任何主体 。 您可以使用jqGrid选项formDeleting指定所有Delete选项,您可以将url参数定义为函数:

 formDeleting: { mtype: "DELETE", url: function (rowid) { return "/RestWithDatabaseConnection/rest/delete/" + rowid; }, ajaxDelOptions: { contentType: "application/json" }, serializeDelData: function () { return ""; } } 

您需要修改/RestWithDatabaseConnection/rest/delete/服务器代码以使用相同的通信协议并从URL获取已删除的ID。

您可以使用navOptions参数来指定navGrid的选项:

 navOptions: { edit: false, add: false } 

searchtext: 'Search'和你使用的其他选项似乎有默认值,我在那里删除)。

为了更接近REST标准,可以使用HTTP PUT操作进行行编辑,使用HTTP POST进行添加新行。 您应该为后端的两个操作实现不同的入口点。 您已经使用/RestWithDatabaseConnection/rest/update ,您可以实现/RestWithDatabaseConnection/rest/create来添加新行。 例如,您可以使用以下内inlineEditing更改来实现方案:

 inlineNavOptions: { add: true, edit: true }, inlineEditing: { url: function (id, editOrAdd) { return "/RestWithDatabaseConnection/rest/" + (editOrAdd === "edit" ? "update" : "create"); }, mtype: function (editOrAdd) { return editOrAdd === "edit" ? "PUT" : "POST"; }, keys: true, serializeSaveData: function (postData) { return JSON.stringify(dataToSend); }, aftersavefunc: function () { $(this).trigger("reloadGrid", [{current: true, fromServer: true}]); }, addParams: { addRowParams: { position: "last", serializeSaveData: function (postData) { var dataToSend = $.extend({}, postData); // don't send any id in case of creating new row // or to send `0`: delete dataToSend.id; // or dataToSend.id = 0; return JSON.stringify(dataToSend); } } } } 
Interesting Posts