<f:ajax render =“@ none”execute =“@ this”>并以较大的形式减少提交的帖子数据大小

时间:2017-07-28 06:38:35

标签: jsf jsf-2.3

我有一个巨大的jsf页面,底层的ViewScopeBean包含一个非常大的表单(包含近100个输入),带有许多ajaxified输入字段。对于每个输入字段,我只提交当前值并且不提供任何内容。所以只是这种形式的示例字段:

<h:selectBooleanCheckbox id="includeForeignCurrencies" 
                         value="#{RatingManagerBean.formData.foreignCurrencies}"
                         action="#{RatingManagerBean.calculate}">

    <f:ajax render="@none" execute="includeForeignCurrencies"/> 

</h:selectBooleanCheckbox>

在ajax后我检查例如firebug中的开发人员工具,并认识到提交的帖子数据大小最高为 2 kb 。然后我选择行,选择“复制发布数据”并将其粘贴到编辑器中:

提交该表单的每个字段,尽管我只对当前更改的字段感兴趣:

form-search=form-search
countryCode=AT
ratingType={"type":"COUNTRY"}
averageRating
minAmount
maxAmount
averageAmount
company
location
staffResponsible
staffResponsible2
requestDate
reminderDate
acutalCurrency
compareCurrencies   

 //70 other empty form-ids....

includeForeignCurrencies=on

javax.faces.ViewState=1825148886808299106:-354534052529224349
javax.faces.source=includeForeignCurrencies
javax.faces.partial.event=click
javax.faces.partial.execute=includeForeignCurrencies
javax.faces.behavior.event=valueChange
javax.faces.partial.ajax=true

有没有办法减少发布的数据,如:

includeForeignCurrencies=on

javax.faces.ViewState=1825148886808299106:-354534052529224349
javax.faces.source=includeForeignCurrencies
javax.faces.partial.event=click
javax.faces.partial.execute=includeForeignCurrencies
javax.faces.behavior.event=valueChange
javax.faces.partial.ajax=true

甚至只是

includeForeignCurrencies=on

提前感谢!

解决方法:

作为一种可能的解决方法,我想出了禁用所有未触发帖子的输入并在ajax完成时重新启用它们。 发布数据大小现在是200 B而不是2000 B.

因为我不确定这是否会引起任何其他问题,这是问题的更新,而不是答案。

<h:selectBooleanCheckbox id="includeForeignCurrencies" 
                     value="#{RatingManagerBean.formData.foreignCurrencies}"
                     action="#{RatingManagerBean.calculate}"
                     onclick="prepareAjaxPost(this)">

<f:ajax render="@none" execute="includeForeignCurrencies" onevent="ajaxPostComplete"/> 

</h:selectBooleanCheckbox>

的javascript / jQuery的:

function prepareAjaxPost(source){   

   //keep already disabled inputs disabled
   $("#form-search :input:not(:disabled)").filter(function() {      

        return !this.id.match(/javax.faces.ViewState/); //all inputs ignoring jsf- viewstate ones

    }).each(function(){
          var input = $(this);
          var others = input.not(source); 
          others.attr("disabled", true);
          others.addClass("blocked"); //some style that has no visual effect for users

    });
}

function ajaxPostComplete(data) {
    switch (data.status) {      
        case "success": {

           $("#form-search :input.blocked").each(function(){
              $(this).attr("disabled",false).removeClass("blocked");     
           });

           break;
        }
}

1 个答案:

答案 0 :(得分:3)

首先阅读结尾处的“编辑2:”

我知道你的问题是关于JSF 2.3的,但出于好奇,我想看看它是否可以在普通的JSF中完成。好消息是,通过覆盖jsf.js函数,至少可以看到Mojarra 2.2.8(没有任何其他环境可以快速完成)。并且...如果你想让它成为'条件',那么不是每个输入/按钮/ ...,还要添加一个直通属性。

我检查/调试了Mojarra jsf.js文件的源代码,并注意到只有在创建完整的查询字符串后才能删除/过滤掉字段,而不是PrimeFaces方式,它们会阻止字段被“预先”添加。可以执行此操作的函数是getViewState,并通过覆盖它,调用原始函数并在返回结果之前添加一些逻辑,这一切似乎都有效。缺点是它可能过滤掉很多(见最后的评论)

要添加到代码中的js(确保在 jsf.js之后加载

<强> partialSubmit.js

var orgViewState = jsf.getViewState;
jsf.getViewState = function(form) {

    var myViewState = orgViewState(form);

    var eventSource = this.ajax.request.arguments[0];
    // Read partialSubmit from the input (Mojarra puts attributes without camelcasing in the html, so pt:partialSubmit in the xhtml becomes partialsubmit in the html 
    var partialSubmit = eventSource.attributes['partialsubmit'];

    //If not on the input, check the form
    if (partialSubmit === undefined) {
        partialSubmit = form.attributes['partialsubmit'];
    } 

    if (partialSubmit != undefined && partialSubmit.value === "true") {
        var params = myViewState.split("&");
        var kvpairs = [];
        var ids = [];
        // Create list of id's based on the 'execute' list
        var execute = this.ajax.request.arguments[2].execute;
        // TODO check for other replacements (@parent, @form)?
        execute = execute.replace('@this', eventSource.id);
        execute = execute.replace('@none', "");

        var ids = execute.split(" ");
        for(j=0;j<ids.length;j++) {
            var id = ids[j];
            for (i=0;i<params.length; i++) {
                var kv = params[i].split("=");
                if (kv[0] === kv[1] || //to pass on the form id value keypair. Not sure this is needed
                    kv[0] === "javax.faces.ViewState" || // check if other 'relevant' fields need to be passed on (viewid, conversationid etc... How to detect these?
                    encodeURIComponent(id) === kv[0]) {
                    kvpairs.push("&"+params[i]);
                }
            }
        }
        return kvpairs.join("");
    } else {
        return myViewState;
    }
}

下面提供了使用示例。在此示例中,第一个和最后一个输入字段已将pt:partialSubmt="true"添加到输入中(在添加到ajax标记时无法使其工作)。还有两个启用了ajax h:commandButtons,一个带有部分提交,一个没有部分提交。使用浏览器开发人员工具,您可以看到使用它们时的差异

<强> partialSubmitDemo.xhtml

<html xmlns="http://www.w3.org/1999/xhtml"
    xmlns:ui="http://java.sun.com/jsf/facelets"
    xmlns:h="http://java.sun.com/jsf/html"
    xmlns:f="http://java.sun.com/jsf/core"
    xmlns:p="http://primefaces.org/ui"
    xmlns:pt="http://xmlns.jcp.org/jsf/passthrough">

    <h:head />

    <h:body>
        <!-- adding pt:partialSubmit="true" to the form makes it work for every input/button in the form -->
        <h:form>

            <h:panelGrid columns="4" border="1" id="panel">
                <h:outputText value="label" />
                <h:outputText value="input" />
                <h:outputText value="single ajax" />
                <h:outputText value="all ajax" />

                <h:outputLabel value="inp0" />
                <h:inputText value="#{sessionScope['inp0']}" id="inp0" pt:partialsubmit="true">
                    <f:ajax render="aout0" execute="@this" />
                </h:inputText>
                <h:outputText value="#{sessionScope['inp0']}" id="aout0" />
                <h:outputText value="#{sessionScope['inp0']}" id="out0" />

                <h:outputLabel value="inp1" />
                <h:inputText value="#{sessionScope['inp1']}" id="inp1">
                    <f:ajax render="aout1" execute="@this" />
                </h:inputText>
                <h:outputText value="#{sessionScope['inp1']}" id="aout1" />
                <h:outputText value="#{sessionScope['inp1']}" id="out1" />

                <h:outputLabel value="inp2" />
                <h:inputText value="#{sessionScope['inp2']}" id="inp2">
                    <f:ajax render="aout2" execute="@this" />
                </h:inputText>
                <h:outputText value="#{sessionScope['inp2']}" id="aout2" />
                <h:outputText value="#{sessionScope['inp2']}" id="out2" />

                <h:outputLabel value="inp3" />
                <h:inputText value="#{sessionScope['inp3']}" id="inp3">
                    <f:ajax render="aout3" execute="@this" />
                </h:inputText>
                <h:outputText value="#{sessionScope['inp3']}" id="aout3" />
                <h:outputText value="#{sessionScope['inp3']}" id="out3" />

                <h:outputLabel value="inp4" />
                <h:inputText value="#{sessionScope['inp4']}" id="inp4">
                    <f:ajax render="aout4" execute="@this" />
                </h:inputText>
                <h:outputText value="#{sessionScope['inp4']}" id="aout4" />
                <h:outputText value="#{sessionScope['inp4']}" id="out4" />

                <h:outputLabel value="inp5" />
                <h:inputText value="#{sessionScope['inp5']}" id="inp5">
                    <f:ajax render="aout5" execute="@this" />
                </h:inputText>
                <h:outputText value="#{sessionScope['inp5']}" id="aout5" />
                <h:outputText value="#{sessionScope['inp5']}" id="out5" />

                <h:outputLabel value="inp6" />
                <h:inputText value="#{sessionScope['inp6']}" id="inp6">
                    <f:ajax render="aout6" execute="@this" />
                </h:inputText>
                <h:outputText value="#{sessionScope['inp6']}" id="aout6" />
                <h:outputText value="#{sessionScope['inp6']}" id="out6" />

                <h:outputLabel value="inp7" />
                <h:inputText value="#{sessionScope['inp7']}" id="inp7">
                    <f:ajax render="aout7" execute="@this" />
                </h:inputText>
                <h:outputText value="#{sessionScope['inp7']}" id="aout7" />
                <h:outputText value="#{sessionScope['inp7']}" id="out7" />

                <h:outputLabel value="inp8" />
                <h:inputText value="#{sessionScope['inp8']}" id="inp8">
                    <f:ajax render="aout8" execute="@this" />
                </h:inputText>
                <h:outputText value="#{sessionScope['inp8']}" id="aout8" />
                <h:outputText value="#{sessionScope['inp8']}" id="out8" />

                <h:outputLabel value="inp9" />
                <h:inputText value="#{sessionScope['inp9']}" id="inp9"
                    pt:partialsubmit="true">
                    <f:ajax render="aout9" execute="@this" />
                </h:inputText>
                <h:outputText value="#{sessionScope['inp9']}" id="aout9" />
                <h:outputText value="#{sessionScope['inp9']}" id="out9" />

            </h:panelGrid>

            <h:commandButton value="Update all ajax partial submit" pt:partialsubmit="true">
                <f:ajax render="out0 out1 out2 out3 out4 out5 out6 out7 out8 out9" execute="@this" />
            </h:commandButton>

            <h:commandButton value="Update all ajax full submit">
                <f:ajax render="out0 out1 out2 out3 out4 out5 out6 out7 out8 out9" execute="@this" />
            </h:commandButton>
            <h:commandButton value="Clear all" pt:partialsubmit="true">
                <f:setPropertyActionListener value="" target="#{sessionScope['inp0']}" />
                <f:setPropertyActionListener value="" target="#{sessionScope['inp1']}" />
                <f:setPropertyActionListener value="" target="#{sessionScope['inp2']}" />
                <f:setPropertyActionListener value="" target="#{sessionScope['inp3']}" />
                <f:setPropertyActionListener value="" target="#{sessionScope['inp4']}" />
                <f:setPropertyActionListener value="" target="#{sessionScope['inp5']}" />
                <f:setPropertyActionListener value="" target="#{sessionScope['inp6']}" />
                <f:setPropertyActionListener value="" target="#{sessionScope['inp7']}" />
                <f:setPropertyActionListener value="" target="#{sessionScope['inp8']}" />
                <f:setPropertyActionListener value="" target="#{sessionScope['inp9']}" />
                <f:ajax render="panel" execute="@this" />
            </h:commandButton>

        </h:form>

        <script src="partialSubmit.js" type="text/javascript" />

    </h:body>
</html>

你可以在javascript代码中看到它需要优化的一些注释,但它是一个很好的工作开始/开始。

修改

  • 添加了将pt:partialSubmit="true"添加到<h:form>的选项,因此请使用表格
  • 调查使用OmniFaces <o:form includeRequestParams="true">传递现有请求参数,并且由于它们不在表单字段的'viewState'键值​​对中,因此不会被剥离。

编辑2:

伟大的OmniFaces项目将在即将发布的3.0版本中实现。 A commit由@BalusC制作,因为这个问题而关闭issue 394我在那里创建的https://regex101.com/r/cW2Is3/4在5天内使用

<o:form> 

将默认为partialSubmit