免费 jqgrid 升级后如何修复表单编辑中的重复表单键 post
How to fix duplicate form key post in form edit after free jqgrid upgrade
最近从 github post 从表单编辑发送图像密钥两次。
例如使用下面的测试用例 post 数据包含:
------WebKitFormBoundaryHjeY1fJPAaaXE56n
Content-Disposition: form-data; name="Pilt"; filename="eeva.bmp"
Content-Type: image/bmp
------WebKitFormBoundaryHjeY1fJPAaaXE56n
Content-Disposition: form-data; name="_image_Pilt"
------WebKitFormBoundaryHjeY1fJPAaaXE56n
Content-Disposition: form-data; name="Pilt"
请注意 name="Pilt"
出现了两次。
要复制,请打开下面 Chrome、select 行中的代码,开始表单编辑,select 一些图像,激活 chrome 开发者工具,然后按提交按钮。
请求负载显示重复的 Pilt 密钥被 posted.
如何解决此问题,以便 post 上没有像早期版本中那样的重复键?
测试用例:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>
<meta name="author" content="Oleg Kiriljuk">
<link rel="stylesheet" href="http://ajax.googleapis.com/ajax/libs/jqueryui/1.11.4/themes/redmond/jquery-ui.css">
<link rel="stylesheet" href="http://netdna.bootstrapcdn.com/font-awesome/4.4.0/css/font-awesome.min.css">
<!--<link rel="stylesheet" href="jqGrid/css/ui.jqgrid.css">-->
<link rel="stylesheet" href="http://rawgit.com/free-jqgrid/jqGrid/master/css/ui.jqgrid.css">
<style>
html, body { font-size: 75%; }
</style>
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.11.3/jquery.min.js"></script>
<script src="http://ajax.googleapis.com/ajax/libs/jqueryui/1.11.4/jquery-ui.min.js"></script>
<script src="http://malsup.github.io/jquery.form.js"></script>
<script>
$.jgrid = $.jgrid || {};
$.jgrid.no_legacy_api = true;
$.jgrid.useJSON = true;
</script>
<!--<script src="jqGrid/js/jquery.jqGrid.src.js"></script>-->
<script src="http://rawgit.com/free-jqgrid/jqGrid/master/js/jquery.jqgrid.src.js"></script>
<script>
//<![CDATA[
/*global $ */
/*jslint browser: true */
$(function () {
"use strict";
/*global $ */
/*jslint plusplus: true, browser: true, eqeq: true */
$(function () {
"use strict";
var mydata = [
{ id: "10", _image_Pilt: null, invdate: "2013-11-01", name: "'", note: "note", amount: "200.00", tax: "10.00", closed: true, ship_via: "TN", total: "210.00" }
],
editSettings = {
url: null,
useDataProxy: true,
dataProxy: function (opt, args) { dataProxyAjax(opt, args, 'Artpilt'); },
checkOnUpdate: true,
reloadAfterSubmit: false,
closeOnEscape: true,
savekey: [true, 13],
closeAfterEdit: true
},
addSettings = {
checkOnUpdate: true,
reloadAfterSubmit: false,
savekey: [true, 13],
closeOnEscape: true,
closeAfterAdd: true
},
delSettings = {
},
initDateEdit = function (elem) {
setTimeout(function () {
$(elem).datepicker({
dateFormat: "dd-M-yy",
showOn: "button",
changeYear: true,
changeMonth: true,
showButtonPanel: true,
showWeek: true
});
}, 50);
},
initDateSearch = function (elem) {
setTimeout(function () {
$(elem).datepicker({
dateFormat: "dd-M-yy",
changeYear: true,
changeMonth: true,
showButtonPanel: true,
showWeek: true
});
}, 50);
},
removeTheOptionAll = function (elem) {
},
dataProxyAjax = function (opts, act, entity) {
//opts.url = $grid.jqGrid('getGridParam', 'url');
if (opts.data._oper === "edit") {
opts.url = 'http://httpbin.org/status/200';
}
else {
opts.url = 'http://httpbin.org/status/201';
}
opts.iframe = true;
var $form = $('#FrmGrid_' + $grid.jqGrid('getGridParam', 'id')),
ele;
//use normal ajax-call when no files to upload
// if ($form.find(':file[value!=""]').size() == 0) {
// $.ajax(opts);
// return;
// }
//Prevent non-file inputs double serialization
ele = $form.find('INPUT,TEXTAREA,SELECT').not(':file');
ele.each(function () {
$(this).data('name', $(this).attr('name'));
$(this).removeAttr('name');
});
//Send only previously generated data + files
$form.ajaxSubmit(opts);
//Set names back after form being submitted
setTimeout(function () {
ele.each(function () {
$(this).attr('name', $(this).data('name'));
});
}, 200);
},
$grid= $("#list")
;
$("#list").jqGrid({
useDataproxy: true,
dataProxy: function (opt, args) { dataProxyAjax(opt, args, 'Artpilt'); },
datatype: "local",
data: mydata,
colNames: ["", "Image","Select image", "Client", "Date", "Amount", "Tax", "Total", "Closed", "Shipped via", "Notes"],
colModel: [
{name: "act", template: "actions"},
{ "label": "Pilt", "name": "_image_Pilt", "edittype": "image", "editoptions": {
"src": ""
}, "editable": true, "formatter": function (cell, options, row) {
return '<img src=\"https://placehold.it/100x100\" />';
}
, "search": false, "title": "", "width": 54
}, { "edittype": "file", "label": "", "name": "Pilt", "search": false, "title": "", "width": 54, "hidden": true, "editrules": { "edithidden": true }, "editable": true },
{ name: "name", width: 60, editrules: { required: true } },
{name: "invdate", width: 80, align: "center", sorttype: "date",
formatter: "date", formatoptions: {newformat: "d-M-Y", reformatAfterEdit: true},
editoptions: {dataInit: initDateEdit, size: 14},
searchoptions: {dataInit: initDateSearch}},
{
name: "amount", width: 62, template: "number",
formatter: "number", formatoptions: { decimalSeparator: ",", thousandsSeparator: " ", decimalPlaces: 2, defaultValue: '0,00' },
editoptions: {
maxlength: 7,
type: "number",
max: "9999",
dataEvents: [
{
type: "blur",
fn: function (e) {
if (e.target.checkValidity()) {
$(e.target).removeClass("ui-state-error");
} else {
$(e.target).addClass("ui-state-error");
alert(e.target.validationMessage);
$(e.target).focus();
}
}
}
]
}
},
//{ name: "amount", width: 70, formatter: "number", align: "right" },
{ name: "tax", width: 50, formatter: "number", align: "right" },
{name: "total", width: 60, formatter: "number", align: "right"},
{name: "closed", width: 70, align: "center", formatter: "checkbox",
edittype: "checkbox", editoptions: {value: "Yes:No", defaultValue: "Yes"},
stype: "select",
searchoptions: {
sopt: ["eq", "ne"],
value: ":All;true:Yes;false:No",
dataInit: removeTheOptionAll
}},
{name: "ship_via", width: 100, align: "center", formatter: "select",
edittype: "select", editoptions: {value: "FE:FedEx;TN:TNT;IN:Intim", defaultValue: "TN"},
stype: "select",
searchoptions: {
sopt: ["eq", "ne"],
value: ":All;FE:FedEx;TN:TNT;IN:Intim",
dataInit: removeTheOptionAll
}},
{name: "note", width: 60, sortable: false, edittype: "textarea"}
],
cmTemplate: {editable: true, searchoptions: {clearSearch: false }},
rowNum: 10,
rowList: [5, 10, 20],
pager: true,
gridview: true,
rownumbers: true,
autoencode: true,
ignoreCase: true,
sortname: "invdate",
viewrecords: true,
sortorder: "desc",
caption: "Demonstrates implementating of local form editing",
height: "100%",
editurl: "http://httpbin.org/status/200",
//editurl: "clientArray",
}).jqGrid("navGrid", {}, editSettings, addSettings, delSettings, {
multipleSearch: true,
overlay: false,
onClose: function () {
// if we close the search dialog during the datapicker are opened
// the datepicker will stay opened. To fix this we have to hide
// the div used by datepicker
$("div#ui-datepicker-div.ui-datepicker").hide();
}
}).jqGrid("filterToolbar", { defaultSearch: "cn" });
});
});
//]]>
</script>
</head>
<body>
<div id="outerDiv" style="margin: 5px;">
<table id="list"></table>
</div>
</body>
</html>
您使用 useDataproxy
选项。因此 jqGrid 根本不发送任何数据。如果存在一些问题,那么应该可能在 dataProxyAjax
的代码或 jquery.form.js
的代码中。 jqGrid 只是准备 postdata
对象中的数据。该对象仅包含一个 属性,名称为 Pilt
。因此,"duplicate form key post" 的任何问题都可能不是免费 jqGrid 中的错误。
如果您希望免费的 jqGrid 使用与以前相同的输出字符编码,那么您只需要包括 autoEncodeOnEdit: true
jqGrid 选项。此外,您可以通过使用 serializeEditData
.
来使用任何自定义编码
更新: 问题在于使用 jQuery.val()
来填写 Add/Edit 表单的所有字段。引入更改是为了支持 HTML5 具有新类型值的输入元素(number
、color
、range
等)。作为副作用,jqGrid 填充了 image
和 file
类型的字段。这是所报告问题的根源。
问题应该在发布后得到解决 the commit。
最近从 github post 从表单编辑发送图像密钥两次。
例如使用下面的测试用例 post 数据包含:
------WebKitFormBoundaryHjeY1fJPAaaXE56n
Content-Disposition: form-data; name="Pilt"; filename="eeva.bmp"
Content-Type: image/bmp
------WebKitFormBoundaryHjeY1fJPAaaXE56n
Content-Disposition: form-data; name="_image_Pilt"
------WebKitFormBoundaryHjeY1fJPAaaXE56n
Content-Disposition: form-data; name="Pilt"
请注意 name="Pilt"
出现了两次。
要复制,请打开下面 Chrome、select 行中的代码,开始表单编辑,select 一些图像,激活 chrome 开发者工具,然后按提交按钮。 请求负载显示重复的 Pilt 密钥被 posted.
如何解决此问题,以便 post 上没有像早期版本中那样的重复键?
测试用例:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>
<meta name="author" content="Oleg Kiriljuk">
<link rel="stylesheet" href="http://ajax.googleapis.com/ajax/libs/jqueryui/1.11.4/themes/redmond/jquery-ui.css">
<link rel="stylesheet" href="http://netdna.bootstrapcdn.com/font-awesome/4.4.0/css/font-awesome.min.css">
<!--<link rel="stylesheet" href="jqGrid/css/ui.jqgrid.css">-->
<link rel="stylesheet" href="http://rawgit.com/free-jqgrid/jqGrid/master/css/ui.jqgrid.css">
<style>
html, body { font-size: 75%; }
</style>
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.11.3/jquery.min.js"></script>
<script src="http://ajax.googleapis.com/ajax/libs/jqueryui/1.11.4/jquery-ui.min.js"></script>
<script src="http://malsup.github.io/jquery.form.js"></script>
<script>
$.jgrid = $.jgrid || {};
$.jgrid.no_legacy_api = true;
$.jgrid.useJSON = true;
</script>
<!--<script src="jqGrid/js/jquery.jqGrid.src.js"></script>-->
<script src="http://rawgit.com/free-jqgrid/jqGrid/master/js/jquery.jqgrid.src.js"></script>
<script>
//<![CDATA[
/*global $ */
/*jslint browser: true */
$(function () {
"use strict";
/*global $ */
/*jslint plusplus: true, browser: true, eqeq: true */
$(function () {
"use strict";
var mydata = [
{ id: "10", _image_Pilt: null, invdate: "2013-11-01", name: "'", note: "note", amount: "200.00", tax: "10.00", closed: true, ship_via: "TN", total: "210.00" }
],
editSettings = {
url: null,
useDataProxy: true,
dataProxy: function (opt, args) { dataProxyAjax(opt, args, 'Artpilt'); },
checkOnUpdate: true,
reloadAfterSubmit: false,
closeOnEscape: true,
savekey: [true, 13],
closeAfterEdit: true
},
addSettings = {
checkOnUpdate: true,
reloadAfterSubmit: false,
savekey: [true, 13],
closeOnEscape: true,
closeAfterAdd: true
},
delSettings = {
},
initDateEdit = function (elem) {
setTimeout(function () {
$(elem).datepicker({
dateFormat: "dd-M-yy",
showOn: "button",
changeYear: true,
changeMonth: true,
showButtonPanel: true,
showWeek: true
});
}, 50);
},
initDateSearch = function (elem) {
setTimeout(function () {
$(elem).datepicker({
dateFormat: "dd-M-yy",
changeYear: true,
changeMonth: true,
showButtonPanel: true,
showWeek: true
});
}, 50);
},
removeTheOptionAll = function (elem) {
},
dataProxyAjax = function (opts, act, entity) {
//opts.url = $grid.jqGrid('getGridParam', 'url');
if (opts.data._oper === "edit") {
opts.url = 'http://httpbin.org/status/200';
}
else {
opts.url = 'http://httpbin.org/status/201';
}
opts.iframe = true;
var $form = $('#FrmGrid_' + $grid.jqGrid('getGridParam', 'id')),
ele;
//use normal ajax-call when no files to upload
// if ($form.find(':file[value!=""]').size() == 0) {
// $.ajax(opts);
// return;
// }
//Prevent non-file inputs double serialization
ele = $form.find('INPUT,TEXTAREA,SELECT').not(':file');
ele.each(function () {
$(this).data('name', $(this).attr('name'));
$(this).removeAttr('name');
});
//Send only previously generated data + files
$form.ajaxSubmit(opts);
//Set names back after form being submitted
setTimeout(function () {
ele.each(function () {
$(this).attr('name', $(this).data('name'));
});
}, 200);
},
$grid= $("#list")
;
$("#list").jqGrid({
useDataproxy: true,
dataProxy: function (opt, args) { dataProxyAjax(opt, args, 'Artpilt'); },
datatype: "local",
data: mydata,
colNames: ["", "Image","Select image", "Client", "Date", "Amount", "Tax", "Total", "Closed", "Shipped via", "Notes"],
colModel: [
{name: "act", template: "actions"},
{ "label": "Pilt", "name": "_image_Pilt", "edittype": "image", "editoptions": {
"src": ""
}, "editable": true, "formatter": function (cell, options, row) {
return '<img src=\"https://placehold.it/100x100\" />';
}
, "search": false, "title": "", "width": 54
}, { "edittype": "file", "label": "", "name": "Pilt", "search": false, "title": "", "width": 54, "hidden": true, "editrules": { "edithidden": true }, "editable": true },
{ name: "name", width: 60, editrules: { required: true } },
{name: "invdate", width: 80, align: "center", sorttype: "date",
formatter: "date", formatoptions: {newformat: "d-M-Y", reformatAfterEdit: true},
editoptions: {dataInit: initDateEdit, size: 14},
searchoptions: {dataInit: initDateSearch}},
{
name: "amount", width: 62, template: "number",
formatter: "number", formatoptions: { decimalSeparator: ",", thousandsSeparator: " ", decimalPlaces: 2, defaultValue: '0,00' },
editoptions: {
maxlength: 7,
type: "number",
max: "9999",
dataEvents: [
{
type: "blur",
fn: function (e) {
if (e.target.checkValidity()) {
$(e.target).removeClass("ui-state-error");
} else {
$(e.target).addClass("ui-state-error");
alert(e.target.validationMessage);
$(e.target).focus();
}
}
}
]
}
},
//{ name: "amount", width: 70, formatter: "number", align: "right" },
{ name: "tax", width: 50, formatter: "number", align: "right" },
{name: "total", width: 60, formatter: "number", align: "right"},
{name: "closed", width: 70, align: "center", formatter: "checkbox",
edittype: "checkbox", editoptions: {value: "Yes:No", defaultValue: "Yes"},
stype: "select",
searchoptions: {
sopt: ["eq", "ne"],
value: ":All;true:Yes;false:No",
dataInit: removeTheOptionAll
}},
{name: "ship_via", width: 100, align: "center", formatter: "select",
edittype: "select", editoptions: {value: "FE:FedEx;TN:TNT;IN:Intim", defaultValue: "TN"},
stype: "select",
searchoptions: {
sopt: ["eq", "ne"],
value: ":All;FE:FedEx;TN:TNT;IN:Intim",
dataInit: removeTheOptionAll
}},
{name: "note", width: 60, sortable: false, edittype: "textarea"}
],
cmTemplate: {editable: true, searchoptions: {clearSearch: false }},
rowNum: 10,
rowList: [5, 10, 20],
pager: true,
gridview: true,
rownumbers: true,
autoencode: true,
ignoreCase: true,
sortname: "invdate",
viewrecords: true,
sortorder: "desc",
caption: "Demonstrates implementating of local form editing",
height: "100%",
editurl: "http://httpbin.org/status/200",
//editurl: "clientArray",
}).jqGrid("navGrid", {}, editSettings, addSettings, delSettings, {
multipleSearch: true,
overlay: false,
onClose: function () {
// if we close the search dialog during the datapicker are opened
// the datepicker will stay opened. To fix this we have to hide
// the div used by datepicker
$("div#ui-datepicker-div.ui-datepicker").hide();
}
}).jqGrid("filterToolbar", { defaultSearch: "cn" });
});
});
//]]>
</script>
</head>
<body>
<div id="outerDiv" style="margin: 5px;">
<table id="list"></table>
</div>
</body>
</html>
您使用 useDataproxy
选项。因此 jqGrid 根本不发送任何数据。如果存在一些问题,那么应该可能在 dataProxyAjax
的代码或 jquery.form.js
的代码中。 jqGrid 只是准备 postdata
对象中的数据。该对象仅包含一个 属性,名称为 Pilt
。因此,"duplicate form key post" 的任何问题都可能不是免费 jqGrid 中的错误。
如果您希望免费的 jqGrid 使用与以前相同的输出字符编码,那么您只需要包括 autoEncodeOnEdit: true
jqGrid 选项。此外,您可以通过使用 serializeEditData
.
更新: 问题在于使用 jQuery.val()
来填写 Add/Edit 表单的所有字段。引入更改是为了支持 HTML5 具有新类型值的输入元素(number
、color
、range
等)。作为副作用,jqGrid 填充了 image
和 file
类型的字段。这是所报告问题的根源。
问题应该在发布后得到解决 the commit。