将50字段表单提交给多个表格;常规POST,AJAX POST或其他?

时间:2013-04-26 13:10:28

标签: jquery mysql performance coldfusion

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,编写一堆验证和更新查询(用表单中的任何内容覆盖每个现有值)并完成它,但我是在这里质问我的判断。

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

我是否应该把这么多的想法投入其中(到目前为止,花了很多时间阅读旧线程......)?这里涉及一些财务数据,所以我主要担心的是可靠性和性能,但我也很好奇是否有其他人遵循的最佳实践?

---实施以下CHOSEN ANSWER后更新---

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

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

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

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

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

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

  5. 在这种情况下可以解决2,3和4点问题,但是在开始时我已经知道了我现在知道的东西,我可能会选择一个包含所有数据的大表,并在表单上简单编辑整行。然后,我会学到更多,所以没有遗憾:)希望这对后来发现这个线程的人有所帮助。

4 个答案:

答案 0 :(得分:2)

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

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

答案 1 :(得分:2)

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

我确保他们对利益相关者/产品所有者/分析师/老板在这方面的功能上的所有人都是可以接受的。因为业务决定逐字段保存是不可接受的(并且在某人使用你的UI之前永远不会考虑这种事情),所以必须对整个事情进行重新编码会非常烦人。 p>

答案 2 :(得分:1)

UPDATE - 初始方法没有很好地处理其他输入类型。我已经更改了代码来处理常见的输入类型以及使用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复制每个表单字段的初始值
  • 触发提交时,将每个字段的当前值与页面加载时保存的值进行比较。
  • 如果有更改,请将数据发回服务器。

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

  • 将数据存储在会话中的服务器上
  • 重新运行用于填充页面的选项,然后将其与表单发布的内容进行比较

答案 3 :(得分: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)就是我从Adam Cameron那里学到的东西,在Stack Overflow上。