【问题标题】:jqgrid: how send and receive row data keeping edit modejqgrid:如何发送和接收行数据保持编辑模式
【发布时间】:2011-10-01 09:28:48
【问题描述】:

jqGrid 有员工姓名和员工 ID 列。

如果employee name 已更改,则应调用服务器验证方法来验证名称。当前行列应根据此方法返回的数据进行更新。

如果employee id 已更改,则应调用服务器验证方法来验证 id。 当前行列应根据此方法返回的数据进行更新。

jqGrid 最好保持在编辑模式,以便用户可以继续更改、接受或拒绝更改。

如何在内联和表单编辑中实现这一点? 我正在考虑以下可能性:

可能性 1.

使用带有自定义验证器的编辑规则,例如

            editrules = new
            {
                custom = true,
                custom_func = function(value, colname) { ??? }
            },

问题:如何从所有列中获取数据、进行同步或异步调用以及使用此调用结果更新列。

可能性 2。

要求用户按 Enter 键保存行。 问题:如何查找更改的列并将此列号传递给服务器。 如何根据服务器响应更新当前行数据。

可能性 3.

使用模糊中描述的奥列格很好的答案 jqgrid change cell value and stay in edit mode

问题:如果输入数据并立即按下输入,模糊不会触发。在这种情况下如何应用模糊?

总而言之,服务器计算/验证应按如下方式进行:

如果更改的列和焦点移出或在更改的列中按下输入以保存,服务器端同步或如果不可能,则应调用异步方法。更改的列名和当前编辑的行值(如在 edit 方法中)作为参数传递给此方法。

此方法返回已编辑行的新值。当前编辑的行值应替换为该方法返回的值。

更新

Oleg 回答假定主键已修改。这个因素并不重要。这是没有主键和其他更新的新版本问题:

jqGrid 有产品条形码和产品名称列。

如果product name 已更改,则应调用服务器验证方法来验证名称。当前行列应根据此方法返回的数据进行更新。

如果product barcode 已更改,则应调用服务器验证方法来验证产品条形码。 当前行列应根据此方法返回的数据进行更新。

jqGrid 应保持在编辑模式,以便用户可以继续更改、接受或拒绝更改。

如何在内联和表单编辑中实现这一点? 我正在考虑以下可能性:

可能性 1.

使用带有自定义验证器的编辑规则,例如

            editrules = new
            {
                custom = true,
                custom_func = function(value, colname) { ??? }
            },

问题:如果输入元素失去焦点,custom_func 不会触发。在保存所有元素之前调用它。所以不能用。

可能性 2。

要求用户按 Enter 键保存行。 问题:如何查找更改的列并将此列号传递给服务器。 保存方法应已知列(名称或条形码更改顺序)并填写不同的列。这看起来不合理。

可能性 3.

使用模糊:

colModel: [{"label":"ProductCode","name":"ProductCode","editoptions":{
"dataEvents":[
{"type":"focus","fn":function(e) { ischanged=false}},
{"type":"change","fn":function(e) {ischanged=true}},
{"type":"keydown","fn":function(e) {ischanged=true }},
{"type":"blur","fn":function(e) { if(ischanged) validate(e)} }
]},

为了实现验证,我在 Oleg 中找到了很好的答案 jqgrid change cell value and stay in edit mode

需求总结:

如果更改的列和焦点移出或在更改的列中按下输入以保存,服务器端同步或如果不可能,则应调用异步方法。更改的列名和当前编辑的行值(如在 edit 方法中)作为参数传递给此方法。

此方法返回已编辑行的新值。当前编辑的行值应替换为该方法返回的值。

更新2

这个问题与并发无关。这是单用户和 jqGrid 问题。更新意味着单个用户更改产品名称或条形码,服务器应提供附加数据(产品 ID 和/或名称/条形码)作为对此的响应。

更新 4

我尝试了下面的代码。 如果用户输入新代码并按 Enter 而不移动到其他行,则不会发生模糊并且不会调用验证。

如果单元格脏了,如何在 jqGrid 保存方法中检测或其他想法如何在按下 Enter 结束编辑时强制运行此代码,而不会失去更改的外键单元格的焦点?

function validate(elem, column) {
ischanged = false;
var i, form, row;
var postData = { _column: column  };
var colModel = $("#grid").jqGrid('getGridParam', 'colModel');
var formEdit = $(elem).is('.FormElement');
// todo: use jQuery serialize()  ???
if (formEdit) {
    form = $(elem).closest('form.FormGrid');
    postData._rowid = $("#grid").jqGrid('getGridParam', 'selrow');
    for (i = 0; i < colModel.length; i++)
        eval('postData.' + colModel[i].name + '="' + $('#' + colModel[i].name + '.FormElement', form[0]).val() + '";');
}
else {
    row = $(elem).closest('tr.jqgrow');
    postData._rowid = row.attr('id');
    for (i = 1; i < colModel.length; i++)
        eval('postData.' + colModel[i].name + '="' + $('#' + postData._rowid + '_' + colModel[i].name).val() + '";');
}
$.ajax('Grid/Validate', {
    data: postData,
    async: false,
    type: 'POST',
    success: function (data, textStatus, jqXHR) {
        for (i = 0; i < data.length; i++) {
            if (formEdit)
                $('#' + data[i].name + '.FormElement', form[0]).val(data[i].value);
            else
                $('#' + postData._rowid + '_' + data[i].name).val(data[i].value);
        }
    }
});
}

colModel 定义为:

{"name":"ProductBarCode",
"editoptions":    {"dataEvents":  
[{"type":"focus","fn":function(e) {ischanged=false}
},
{"type":"change","fn":function(e) {ischanged=true},
{"type":"keydown","fn":function(e) {if(realchangekey()) ischanged=true}
},{"type":"blur","fn":function(e) { if(ischanged) {  validate(       e.target,ProductBarCode')}}
}]},"editable":true}

【问题讨论】:

  • 在我看来,数据的服务器端验证是保存数据的一部分,所以我写了我的答案,其中描述了如何检测特别是并发错误。跨度>

标签: jqgrid


【解决方案1】:

这是一个更容易避免和消除的问题。我必须提醒你我的建议(在 the answer 的 cmets 中)使用 immutable 主键,这样就永远不会改变。数据库表的记录可以被销毁,但新记录不应该有曾经删除过的记录的id。

在任何并发控制实现中,服务器首先能够检测并发问题是很重要的。可能是您的 Web 应用程序的两个(或更多)用户读取了相同的信息,例如有关员工的信息。例如,这些信息可以显示在 jqGrids 中。如果您允许更改员工 ID,那么第一个问题将是检测并发错误。让我们一个用户更改员工 ID,而另一个用户将尝试根据 先前加载的信息修改同一员工。用户提交修改后,服务器应用程序只会收到“编辑”请求,但不会在数据库中找到相应的记录。服务器将不得不发送没有任何细节的错误响应。所以editRowerrorfunceditGridRow 的事件处理程序errorTextFormat 应该触发“reloadGrid”重新加载整个网格包含。

如果您允许编辑主键,那么我可以想象前面描述的更危险的情况。可能是另一个用户不仅将当前编辑行的 id 更改为另一个值,而且还可以更改另一条记录的 id,使其新 id 与当前编辑 id 相同。如果保存行的请求将覆盖另一条记录

为了防止此类问题并简化optimistic concurrency control,可以添加一个额外的列,该列表示可以修改的数据库的每个表中的任何形式的时间戳。我个人使用Microsoft SQL Server 并添加我以前添加的类型为rowversion 的不可为空的列(与之前版本的SQL Server 中的timestamp 类型相同)。 rowversion 的值将与数据一起发送到 jqGrid。将发送到服务器的修改请求将包含rowversion。如果有任何数据将保存在数据库中,则相应的rowversion 列中的相应值将由 SQL 数据库自动修改。服务器可以很容易地使用以下代码检测并发错误

CREATE PROCEDURE dbo.spEmployeesUpdate
    -- @originalRowUpdateTimeStamp used for optimistic concurrency mechanism
    -- it is the value which correspond the data used by the user as the source
    @Id int,
    @EmployeeName varchar(100),
    @originalRowUpdateTimeStamp rowversion,
    @NewRowUpdateTimeStamp rowversion OUTPUT
AS
BEGIN
    -- SET NOCOUNT ON added to prevent extra result sets from
    -- interfering with SELECT statements.
    -- ExecuteNonQuery() returns -1, but it is not an error
    -- one should test @NewRowUpdateTimeStamp for DBNull
    SET NOCOUNT ON;

    UPDATE dbo.Employees
    SET Name = @EmployeeName
    WHERE Id=@Id AND RowUpdateTimeStamp=@originalRowUpdateTimeStamp;

    -- get the new value of the RowUpdateTimeStamp (rowversion)
    -- if the previous update took place
    SET @NewRowUpdateTimeStamp = (SELECT RowUpdateTimeStamp
                                  FROM dbo.Employees
                                  WHERE @@ROWCOUNT > 0 AND Id=@Id)
END

您可以在服务器应用程序的代码中验证输出参数@NewRowUpdateTimeStamp 将由存储过程dbo.spEmployeesUpdate 设置。如果未设置,服务器应用程序可能会抛出 DBConcurrencyException 异常。

所以我认为你应该修改数据库和服务器应用程序代码来实现乐观并发控制。之后,服务器代码应返回带有 HTTP 错误代码的响应,以防并发错误。 editRowerrorfunceditGridRow 的事件处理程序 errorTextFormat 应该重新加载当前修改行的新值。您可以使用the more complex way 或只是重新加载网格并继续修改当前行。在 unchanged rowid 的情况下,您可以轻松找到新加载的行并在网格重新加载后开始对其进行编辑。

在已有的数据库中可以使用

ALTER TABLE dbo.Employees ADD NewId int IDENTITY NOT NULL
ALTER TABLE dbo.Employees ADD RowUpdateTimeStamp rowversion NOT NULL
ALTER TABLE dbo.Employees ADD CONSTRAINT UC_Employees_NewId UNIQUE NONCLUSTERED (NewId)
GO

然后您可以使用NewId 代替 jqGrid 中的 id 或您需要的任何其他地方。 NewId 可以与您当前的主键共存,直到您更新应用程序的其他部分以使用更有效的 NewId

更新:我认为不需要对并发错误实施任何复杂的纠错。在我客户的项目中,需要编辑的数据不能包含任何长文本。因此,描述无法保存当前修改的原因的简单消息就足够了。用户可以手动重新加载完整的网格并验证他编辑的行的当前包含。不要忘记,任何复杂的程序都会给项目带来额外的错误,实施复杂,会增加开发预算,而且大部分额外的投资永远无法得到回报。

如果您确实需要实现编辑行的自动刷新,例如,我永远不会在“on blur”事件中实现单元格验证。相反,可以在editRowerrorfunc 内部或editGridRowerrorTextFormat 事件处理程序内部验证服务器返回并发错误。在并发错误的情况下,可以将当前编辑行的 id 保存在一个变量中,该变量可以在 loadComplete 事件句柄内访问。然后,在显示错误消息后,只需重新加载关于$('#list').trigger('reloadGrid',[{current:true}]) 的网格(请参阅here)。在loadComplete 事件句柄内部可以验证是否设置了中止编辑行的变量。在这种情况下,可以调用editRoweditGridRow 并继续编辑字符串。我认为当当前行改变时,页面的另一行也可以改变。因此,重新加载当前页面比只加载当前单元格或网格的一行数据更好。

【讨论】:

  • 谢谢。我添加了不使用主键的更正问题。并发违规判定是另一个问题。我正在使用 PostgreSql 和自然复合主键。对于并发检测,当前应用程序 DataGrid 显示原始和更改的行值。因此,甚至可以在不使用时间戳列的情况下检测并发性,并且如果主键也发生了变化。我了解 jqGrid 不会发回原始值,因此无法使用此方法。如果是第一个版本的应用程序,我不打算实施并发检测。
  • 非常感谢。我将 update2 添加到问题中,说明该问题与并发无关。这是纯粹的 jqGrid 数据输入问题:更改列后,其他列需要更新。很抱歉造成混乱。
  • @Andrus:最后一次更新看起来像是发回有关当前行的附加信息。典型的服务器响应包含新行的 id(在“添加”操作的情况下)并且在“删除”或“编辑”操作的情况下为空。您可以将任何附加信息发回并使用editRowafterSubmitafterCompletesuccesfunc 参数以用于表单编辑。您可以从服务器响应中获取附加数据并更新网格中的一些数据。在我发回rowversion 的方式中,这是并发检查所需的。您可以发回任何其他信息。
  • 谢谢。我覆盖了整个问题以尽可能清楚。整个任务在 Update3 中。
  • @Andrus:我们在这个问题上有不同的方法。我尝试向您解释如何避免该问题。我可以重复一下,如果当前编辑的数据行发生更改,则当前页面上的许多其他信息也发生更改的概率。所以我认为应该重新加载整个网格。如果您确实想选择另一种方式,您应该从编辑单元格中获取信息,如the anser 中所述
猜你喜欢
  • 2011-03-22
  • 1970-01-01
  • 1970-01-01
  • 2011-07-11
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-01-02
相关资源
最近更新 更多