<f:ajax render="@none" execute="@this"> 并减少提交的 post 大数据大小
<f:ajax render="@none" execute="@this"> and reducing submitted post data size in large form
我有一个巨大的 jsf 页面,其底层 ViewScopeBean 包含一个非常大的表单(包含近 100 个输入)和许多 ajax 化的输入字段。对于每个输入字段,我只提交当前值而不渲染任何内容。所以只是该表单中的示例字段:
<h:selectBooleanCheckbox id="includeForeignCurrencies"
value="#{RatingManagerBean.formData.foreignCurrencies}"
action="#{RatingManagerBean.calculate}">
<f:ajax render="@none" execute="includeForeignCurrencies"/>
</h:selectBooleanCheckbox>
在 ajax post 之后我检查了例如firebug 中的开发人员工具并识别出 post 数据的提交大小高达 2 kb。然后我 select 该行,选择 "copy post data" 并将其粘贴到编辑器中:
该表单的每个字段都已提交,但我只对当前更改的字段感兴趣:
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
有没有办法减少像这样的 posted 数据:
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
提前致谢!
解决方法:
作为一种可能的解决方法,我想出了禁用所有未触发 post 的输入并在 ajax 完成后重新启用它们。 post 数据大小现在是 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;
}
}
开始阅读结尾的'edit 2:'
我知道您的问题是关于 JSF 2.3 的,但出于好奇,我想看看在普通 JSF 中是否完全可行。好消息是,至少对于 Mojarra 2.2.8(手头没有任何其他环境)似乎可以通过覆盖 jsf.js 函数来实现。并且...如果您希望它是 'conditional',那么不是在每个 input/button/... 上,还要添加一个 passthrough 属性。
我 checked/debugged Mojarra jsf.js 文件的源代码并注意到只有在创建完整的查询字符串后才能 strip/filter 出字段,而不是 PrimeFaces 阻止的方式要添加的字段 'upfront'。可以完成此操作的函数是 getViewState 并且通过覆盖它,调用原始函数并在返回结果之前添加一些逻辑,这一切似乎都有效。缺点是它可能会过滤掉很多(见最后的评论)
要添加到代码中的 js(确保加载的是 after 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 代码中看到一些评论,它需要优化,但它是一个很好的工作 start/beginning。
编辑:
- 添加了将
pt:partialSubmit="true"
添加到 <h:form>
的选项,因此可以广泛使用它
- 通过使用 OmniFaces
<o:form includeRequestParams="true">
调查了现有请求参数的传递,并且由于它们不在表单字段的 'viewState' 键值对中,所以它们没有被删除。
编辑 2:
伟大的 OmniFaces 项目将在即将发布的 3.0 版本中包含此功能。 A commit has been made by @BalusC that closed issue 394 由于这个问题,我在那里创建的。 5 天内 使用
<o:form>
将默认为部分提交
我有一个巨大的 jsf 页面,其底层 ViewScopeBean 包含一个非常大的表单(包含近 100 个输入)和许多 ajax 化的输入字段。对于每个输入字段,我只提交当前值而不渲染任何内容。所以只是该表单中的示例字段:
<h:selectBooleanCheckbox id="includeForeignCurrencies"
value="#{RatingManagerBean.formData.foreignCurrencies}"
action="#{RatingManagerBean.calculate}">
<f:ajax render="@none" execute="includeForeignCurrencies"/>
</h:selectBooleanCheckbox>
在 ajax post 之后我检查了例如firebug 中的开发人员工具并识别出 post 数据的提交大小高达 2 kb。然后我 select 该行,选择 "copy post data" 并将其粘贴到编辑器中:
该表单的每个字段都已提交,但我只对当前更改的字段感兴趣:
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
有没有办法减少像这样的 posted 数据:
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
提前致谢!
解决方法:
作为一种可能的解决方法,我想出了禁用所有未触发 post 的输入并在 ajax 完成后重新启用它们。 post 数据大小现在是 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;
}
}
开始阅读结尾的'edit 2:'
我知道您的问题是关于 JSF 2.3 的,但出于好奇,我想看看在普通 JSF 中是否完全可行。好消息是,至少对于 Mojarra 2.2.8(手头没有任何其他环境)似乎可以通过覆盖 jsf.js 函数来实现。并且...如果您希望它是 'conditional',那么不是在每个 input/button/... 上,还要添加一个 passthrough 属性。
我 checked/debugged Mojarra jsf.js 文件的源代码并注意到只有在创建完整的查询字符串后才能 strip/filter 出字段,而不是 PrimeFaces 阻止的方式要添加的字段 'upfront'。可以完成此操作的函数是 getViewState 并且通过覆盖它,调用原始函数并在返回结果之前添加一些逻辑,这一切似乎都有效。缺点是它可能会过滤掉很多(见最后的评论)
要添加到代码中的 js(确保加载的是 after 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 代码中看到一些评论,它需要优化,但它是一个很好的工作 start/beginning。
编辑:
- 添加了将
pt:partialSubmit="true"
添加到<h:form>
的选项,因此可以广泛使用它 - 通过使用 OmniFaces
<o:form includeRequestParams="true">
调查了现有请求参数的传递,并且由于它们不在表单字段的 'viewState' 键值对中,所以它们没有被删除。
编辑 2:
伟大的 OmniFaces 项目将在即将发布的 3.0 版本中包含此功能。 A commit has been made by @BalusC that closed issue 394 由于这个问题,我在那里创建的。 5 天内 使用
<o:form>
将默认为部分提交