5

Stackoverflow 的老读者;第一次发帖,希望你温柔点:)

我在一个页面上有一个表单,其中包含大约 50 个不同类型的字段(复选框/文本/十进制/日期等)。通过一个查询从大约 8 个表中提取值,大致如下:

SELECT * FROM p
LEFT JOIN pd on p.id=pd.id
LEFT JOIN pc on p.id=pc.id
LEFT JOIN pie on p.id=pie.id
etc.
WHERE p.id = xxx

我开始这一天以为我只是在表单上使用一个简单的 POST,编写一堆验证和更新查询(用表单中的任何内容覆盖每个现有值)并完成它,但我质疑我的判断这里。

具体来说,如果现有值没有更改,则覆盖它感觉是错误的,而且我有点担心如果数据库更新中途失败会发生什么(考虑使用事务处理)。我对较小的表单感到满意,但如果员工只更改了 1 或 2 个字段,这感觉就像是白白写了很多东西。我的下一个想法是基于每个字段级别使其成为 AJAX。更改任何字段都会提交更改并保存。感觉它可能更有意义,即使我宁愿尽可能避免使用 js。第三种选择当然是将其转换为具有多个提交按钮的多个表单,例如每个选项卡一个(表单已经分为选项卡),缺点是由于需要更多提交而更频繁地重新加载页面(尽管在这里当然也可以使用 AJAX)。

我是否应该考虑这么多(到目前为止,我花了一天的大部分时间在这里阅读旧线程......)?!这里涉及到一些财务数据,所以我主要关心的是可靠性和性能,但我也很好奇是否有其他人遵循的任何最佳实践?

--- 执行下面选择的答案后更新 ---

作为 SO 的长期读者,我总是很欣赏提出问题的人稍后跟进的线程,所以我想我自己会这样做。不确定协议或格式是否正确。

如上所述,我最终选择了 barnyr 的解决方案,它基本上使用 javascript 将提交时的表单与原始值进行比较,然后将更改发布到 mysql(使用 jquery post)。如果您正在考虑类似的情况,请考虑以下几点:

  1. 如果未选中,jquery 的序列化不会发送复选框/单选值。我看到他们的逻辑,但对我来说这没有意义。我使用了http://tdanemar.wordpress.com/2010/08/24/jquery-serialize-method-and-checkboxes/上的插件来解决这个问题。

  2. 如果您在页面上编辑一个值,然后保存它,然后再次编辑它,回到原始值,您将收到一条“没有改变”消息,与页面加载时设置的初始值相比,没有任何改变。这是合乎逻辑的,但直到我完成并测试之后我才考虑到这一点。我真的看不出有什么办法可以保证保持这个解决方案带来的复杂性而不是简单的“覆盖表单提交上的所有内容”,所以如果你正在构建一个关心你的用户的公共应用程序,我不会建议您使用此方法。

  3. 在规范化方面,这个解决方案很漂亮,因为我可以防止将行添加到链接到包含用户 ID 的主表的表中,除非将内容添加到这些特定字段。但是,如果第 2 点对我来说是一个大问题,那么只需将所有这些值以一个大表格的形式存储在一个大表中,就会大大减少代码的复杂性。我几乎是一个新手 wrt 标准化(所以在干草叉上放轻松),这当然主要是所有数据都以一种形式显示的结果。如果您使用多个表格,则不再适用。

  4. 系统的一部分涉及大量数字,汇总在表单的其他部分中,通过 AJAX 完成所有这些意味着您必须非常小心并清楚用户点击保存时到底发生了什么变化。例如,他们可以更改程序价格,并且总价格也应该更新,但是当您通过 AJAX 提交并且没有适当的重新加载时,您必须将所有这些编码回系统中。

在这种情况下,第 2,3 和第 4 点可以解决,但如果我在开始时知道我现在所知道的,我可能会使用一个包含所有数据的大表,并在表单提交时简单地编辑整行。话又说回来,我学到的东西会少很多,所以没有遗憾:) 希望这对那些在稍后阶段找到这个线程的人有所帮助。

4

4 回答 4

2

好吧,我会将页面上的值与用户更改的值进行比较。然后我会将更改的值发布到服务器,动态创建我的查询并仅更新已更改的字段。

此外,如果您要更新多个表,则绝对应该使用事务。

于 2013-04-26T13:17:42.567 回答
2

除了乔丹的回答,我想说最好的起点是用户的期望。每个不同的选项在技术上都有效,但在保存内容和时间方面都有不同的行为。

我会再次确保他们都可以被利益相关者/产品负责人/分析师/老板接受这一功能的人所接受。不得不重新编码整个事情会很烦人,因为企业认为逐个字段保存是不可接受的(而且在有人使用您的 UI 之前,这种事情永远不会被考虑)

于 2013-04-26T13:38:58.850 回答
1

更新 - 最初的方法没有很好地处理其他输入类型。我已经更改了代码以处理常见的输入类型以及使用 DOM 属性作为初始值,这避免了在页面加载时运行任何代码:

这是链接: http: //jsfiddle.net/rLwca/5/这是更新后的功能:

    //Initial setup no longer needed. the DOM has the default states anyway...

//heres where we filter the elements for ones which have changed
$("#My50PageForm").submit(function(){        
    var elems = $("#My50PageForm :input").filter(function(value){
        var elem=$(this),
            type=this.tagName +"_"+(elem.attr("type")||""); // uniquely name the element tag and type

        switch (type){
            case "INPUT_radio": case "INPUT_checkbox":                    
                return elem.prop("checked")!=elem.prop("defaultChecked");
            case "INPUT_text": case "INPUT_textarea": case "INPUT_":                 
                return elem.val()!=elem.prop("defaultValue");
            case "SELECT_":
                var options=$(this).find('option'),
                    defaultValue=options.first().val(); //use the first element's value as default in case no defaults are set
                options.each(function (i,o) {
                    defaultValue=this.defaultSelected?this.value:defaultValue;
                });
                return $(this).val()!=defaultValue;

            default:
                console.log("type "+type+" not handled");
                return false;
        }
     });

    if(elems.length){
        console.log(elems.serialize());
        return false;
        $.post("http://jsfiddle.net/example.cfm",
               elems.serialize());
    }else{
       alert("nothing changed");   
    }         

    return false;
});

原代码如下:

这是发送更改内容的最小示例的链接:

http://jsfiddle.net/UhGQX/

$(document).ready(function(){
//Copy all form valued into a data attribute called 'original' when the page loads
$("#My50PageForm :input").each(function(elem){
    $(this).data("original",$(this).val());
});

//here's where you check what has changed
$("#My50PageForm").submit(function(){        

    var elems = $("#My50PageForm :input").filter(function(value){
        var elem=$(this),
        original=elem.data("original");
        console.log(original);
        //check that original isn't 'undefined' and that it's been changed
        return original && elem.val()!==original
    });
    if(elems.length){
        //post the data back to your server for processing
        $.post("http://jsfiddle.net/example.cfm",
               elems.serialize());
    }else{
     alert("nothing changed");   
    }         

    return false;
});
});

关键位是:

  • 当页面加载时,使用jQuery复制每个表单字段的初始值
  • 触发提交时,将每个字段的当前值与页面加载时保存的值进行比较。
  • 如果有更改,请将数据发送回服务器。

其他方法可能是,允许发布整个表单:

  • 在会话中将数据存储在服务器上
  • 重新运行您用于填充页面的选择,然后将其与表单发布的内容进行比较
于 2013-04-26T15:20:01.493 回答
1

我会避免基于客户端 onChange 或 onBlur 事件的数据库写入。虽然它很容易编码,但它是基于用户实际上已经决定了该字段的适当值的假设。这是一个不安全的假设。有人校对。其他人中途放弃表格。此外,如果表单字段是一个选择,onChange 事件可能会在它应该触发之前触发。

我也会避免按照 barnyr 的建议将数据存储在会话范围内。用户通过打开新的浏览器选项卡无意中更改了他们的会话变量,这让我们感到很痛苦。

我经常采用的一种方法是创建表单字段的查询对象并使用 QofQ 来决定是否需要更新任何内容。这是一个简单的例子。

<cfscript>
RecordsToUpdate = QueryNew("a");
FormValues = QueryNew("id,name","integer,varchar");
Delim = Chr(30);
</cfscript>

<cfloop list="#form.fieldnames#" index="ThisElement">
<cfset ThisValue = form[ThisElement]>
<cfif left(ThisElement, 12) is "NewCategory_">
 not relevent here
<cfelse>
<cfscript>
ThisId = RemoveChars(ThisElement,1,17);
AddNewRow(FormValues,"id#Delim#name", "#ThisID##Delim##ThisValue#", Delim);
// AddNewRow is a udf
</cfscript>
</cfif>
</cfloop>

<cfquery name="RecordsToUpdate" dbtype="query">
select FormValues.*
from FormValues, GetCategories
where id = CategoryId
and name <> CategoryName
</cfquery>

<cfloop query="RecordsToUpdate">
update query
</cfloop>

顺便说一句,Chr(30) 的使用是我在 Stack Overflow 上从 Adam Cameron 那里学到的。

于 2013-04-26T18:37:15.480 回答