如何仅在加载页面时使用 javascript 调用 JSF 支持 bean 方法
How to call JSF backing bean method using javascript only when The page is loaded
问题是后面的 Bean Methode 被调用了很多次
嗯,我已经读过@BalusC 的这个回答
JSF is a server side language which runs on the webserver upon a HTTP request and produces HTML/CSS/JS code which get returned with the HTTP response. All EL expressions in form of ${} and #{} will be executed in the server side during generating the HTML output. JavaScript is a client side language which runs on the webbrowser and works on the HTML DOM tree. The HTML onclick attribute should specify a JavaScript function which will be executed in the client side on the particular HTML DOM event.
How to call JSF backing bean method only when onclick/oncomplete/on... event occurs and not on page load
所以我明白了为什么我的后台方法被调用了很多次,所以我该如何解决这个问题
这是我的后背 Methode
@ManagedBean (name="villeBean)"
@ViewScoped
public class Villes {
public String listeVilles{
return dao.listeVilles();
}
}
这是我的js代码
if(localStorage['ville'] == null || localStorage['date']==null){
localStorage.setItem('date',new Date());
localStorage.setItem('ville',#{villeBean.listeVilles()});
}
else{
var oldDate = new Date(localStorage['date']).getTime();;
var currentDate = new Date().getTime();
var distance = currentDate - oldDate ;
var days = Math.floor(distance / (1000 * 60 * 60 * 24));
if (days >= 15){
localStorage.setItem('date',date);
localStorage.setItem('ville',#{villeBean.listeVilles()});
}
}
问题是后面的方法被调用了 2 次
你可以使用backbean jsf outputscript组件来做,当你在里面使用jsf EL时javascript代码有多个请求问题是正常的,因为一个是静态的,另一个是动态的。
您可以将 javascript 代码作为 resource/js 文件夹中的一个函数放在一个 js 文件中,并使用这样的托管 bean 调用该函数:
UIOutput output = new UIOutput();
output.setRendererType("javax.faces.resource.Script");
// JS FILE NAME DEFINED IN resource/js folder is put here ( example: myscript.js):
output.getAttributes().put("name", "myscript.js");
output.getAttributes().put("library", "js");
facesContext.getViewRoot().addComponentResource(facesContext, output, "form");
UIOutput script = (UIOutput) facesContext.getApplication()
.createComponent(facesContext,
JAVAX_FACES_OUTPUT_COMPONENT_TYPE,
JAVAX_FACES_TEXT_RENDERER_TYPE);
UIOutput outputScript = (UIOutput) facesContext.getApplication()
.createComponent(facesContext,
JAVAX_FACES_OUTPUT_COMPONENT_TYPE,
DEFAULT_SCRIPT_RENDERER_TYPE);
/** AT HERE YOU SHOULD CALL YOUR JAVA SCRIPT FUNCTION WITH THE PARAMETERS OF YOUR BACK BEAN
let's say that you have created something like that:
function addToLocalStorage(ville){
if(localStorage['ville'] == null || localStorage['date']==null){
localStorage.setItem('date',new Date());
localStorage.setItem('ville',ville});
}
}
So you're going to call the function from backbean like that:
*/
String ville = villeBean.listeVilles();
script.setValue("addToLocalStorage(" + ville + ")");
script.setTransient(true);
script.setId(facesContext.getViewRoot().createUniqueId());
outputScript.getChildren().add(script);
outputScript.setTransient(true);
outputScript.setId(facesContext.getViewRoot().createUniqueId());
facesContext.getViewRoot().addComponentResource(facesContext,
outputScript, TARGET_ATTR);
其他选项是使用 Primefaces 套件中的 RequestContext,这样您就可以从 JSF 管理的 bean 中调用 javascript 函数:
RequestContext.getCurrentInstance().execute("myfunction()");
您的 EL-Expression #{villeBean.listeVilles()}
在您的 javascript 中有两个实例,无论您的 javascript 条件如何,它们都在服务器端呈现(并因此被调用)。
像这样更改您的脚本,以便只调用一次:
var villesList = #{villeBean.listeVilles()};
if(localStorage['ville'] == null || localStorage['date']==null){
localStorage.setItem('date',new Date());
localStorage.setItem('ville', villesList);
}
else{
var oldDate = new Date(localStorage['date']).getTime();;
var currentDate = new Date().getTime();
var distance = currentDate - oldDate ;
var days = Math.floor(distance / (1000 * 60 * 60 * 24));
if (days >= 15){
localStorage.setItem('date',date);
localStorage.setItem('ville', villesList);
}
}
优势:
- 业务逻辑 (
dao.listeVilles()
) 只调用一次(但不能保证...)
- 潜在的巨大列表只发送一次到浏览器,减少了数据传输开销。
无论如何,遵循 Ricardo 和 Holger 对 Why JSF calls getters multiple times 的建议的建议是一个好主意,建议将您的 bean 更改为:
@ManagedBean (name="villeBean")
@ViewScoped
public class Villes {
private String villesList;
public String listeVilles{
if(villesList == null) {
villesList = dao.listeVilles();
}
return villesList;
}
}
优势:
- 业务逻辑 (
dao.listeVilles()
) 仅在 xhtml 文件中出现任意次数的 #{villeBean.listeVilles()}
时调用一次。
风险:
- 在您的 xhtml 中每次出现
#{villeBean.listeVilles()}
仍会呈现和传输潜在的巨大列表。
- 您的视图在使用 AJAX 时可能会看到 old/obsolete 数据,因为这是在同一视图范围内永远不会失效的缓存。
事实证明,您实际上希望在 localStorage
中有一个客户端缓存,并在该客户端缓存有效时阻止业务逻辑调用,您
必须走 AJAX 路:
我建议实施 javascript 有条件地呈现 仅在需要时更新本地存储:
<h:panelGroup id="updateLocalStorageScript" style="display: none;">
<h:outputScript rendered="#{villeBean.updateLocalStorage}">
localStorage.setItem('date',date);
localStorage.setItem('ville', #{villeBean.villesList});
</h:outputScript>
</h:panelGroup>
可以通过此处建议的命令操作触发更新:How to invoke a JSF managed bean on a HTML DOM event using native JavaScript?
<h:form id="frmHidden" style="display: none;">
<h:commandButton id="cmdDoUpdateLocalStorage" action="#{villeBean.doUpdateLocalStorage()}">
<f:ajax execute="@this" render=":updateLocalStorageScript" />
</h:commandButton>
</h:form>
当然你也可以使用p:remoteCommand
,OmniFaces解决方案或上述QA中提出的其他建议。
此命令操作导致 javascript 被呈现并通过仅调用您的业务逻辑一次来初始化列表值。
@ManagedBean (name="villeBean)"
@ViewScoped
public class Villes {
private boolean updateLocalStorage;
private String villesList;
public void doUpdateLocalStorage() {
updateLocalStorage = true;
villesList = dao.listeVilles();
}
public boolean isUpdateLocalStorage() {
return updateLocalStorage;
}
public String getVillesList() {
return villesList;
}
}
从您的 javascript 条件块中触发该命令:
if(localStorage['ville'] == null || localStorage['date']==null){
document.getElementById('frmHidden:cmdDoUpdateLocalStorage').onclick();
}
else{
var oldDate = new Date(localStorage['date']).getTime();;
var currentDate = new Date().getTime();
var distance = currentDate - oldDate ;
var days = Math.floor(distance / (1000 * 60 * 60 * 24));
if (days >= 15){
document.getElementById('frmHidden:cmdDoUpdateLocalStorage').onclick();
}
}
问题是后面的 Bean Methode 被调用了很多次
嗯,我已经读过@BalusC 的这个回答
JSF is a server side language which runs on the webserver upon a HTTP request and produces HTML/CSS/JS code which get returned with the HTTP response. All EL expressions in form of ${} and #{} will be executed in the server side during generating the HTML output. JavaScript is a client side language which runs on the webbrowser and works on the HTML DOM tree. The HTML onclick attribute should specify a JavaScript function which will be executed in the client side on the particular HTML DOM event.
How to call JSF backing bean method only when onclick/oncomplete/on... event occurs and not on page load
所以我明白了为什么我的后台方法被调用了很多次,所以我该如何解决这个问题
这是我的后背 Methode
@ManagedBean (name="villeBean)"
@ViewScoped
public class Villes {
public String listeVilles{
return dao.listeVilles();
}
}
这是我的js代码
if(localStorage['ville'] == null || localStorage['date']==null){
localStorage.setItem('date',new Date());
localStorage.setItem('ville',#{villeBean.listeVilles()});
}
else{
var oldDate = new Date(localStorage['date']).getTime();;
var currentDate = new Date().getTime();
var distance = currentDate - oldDate ;
var days = Math.floor(distance / (1000 * 60 * 60 * 24));
if (days >= 15){
localStorage.setItem('date',date);
localStorage.setItem('ville',#{villeBean.listeVilles()});
}
}
问题是后面的方法被调用了 2 次
你可以使用backbean jsf outputscript组件来做,当你在里面使用jsf EL时javascript代码有多个请求问题是正常的,因为一个是静态的,另一个是动态的。
您可以将 javascript 代码作为 resource/js 文件夹中的一个函数放在一个 js 文件中,并使用这样的托管 bean 调用该函数:
UIOutput output = new UIOutput();
output.setRendererType("javax.faces.resource.Script");
// JS FILE NAME DEFINED IN resource/js folder is put here ( example: myscript.js):
output.getAttributes().put("name", "myscript.js");
output.getAttributes().put("library", "js");
facesContext.getViewRoot().addComponentResource(facesContext, output, "form");
UIOutput script = (UIOutput) facesContext.getApplication()
.createComponent(facesContext,
JAVAX_FACES_OUTPUT_COMPONENT_TYPE,
JAVAX_FACES_TEXT_RENDERER_TYPE);
UIOutput outputScript = (UIOutput) facesContext.getApplication()
.createComponent(facesContext,
JAVAX_FACES_OUTPUT_COMPONENT_TYPE,
DEFAULT_SCRIPT_RENDERER_TYPE);
/** AT HERE YOU SHOULD CALL YOUR JAVA SCRIPT FUNCTION WITH THE PARAMETERS OF YOUR BACK BEAN
let's say that you have created something like that:
function addToLocalStorage(ville){
if(localStorage['ville'] == null || localStorage['date']==null){
localStorage.setItem('date',new Date());
localStorage.setItem('ville',ville});
}
}
So you're going to call the function from backbean like that:
*/
String ville = villeBean.listeVilles();
script.setValue("addToLocalStorage(" + ville + ")");
script.setTransient(true);
script.setId(facesContext.getViewRoot().createUniqueId());
outputScript.getChildren().add(script);
outputScript.setTransient(true);
outputScript.setId(facesContext.getViewRoot().createUniqueId());
facesContext.getViewRoot().addComponentResource(facesContext,
outputScript, TARGET_ATTR);
其他选项是使用 Primefaces 套件中的 RequestContext,这样您就可以从 JSF 管理的 bean 中调用 javascript 函数:
RequestContext.getCurrentInstance().execute("myfunction()");
您的 EL-Expression #{villeBean.listeVilles()}
在您的 javascript 中有两个实例,无论您的 javascript 条件如何,它们都在服务器端呈现(并因此被调用)。
像这样更改您的脚本,以便只调用一次:
var villesList = #{villeBean.listeVilles()};
if(localStorage['ville'] == null || localStorage['date']==null){
localStorage.setItem('date',new Date());
localStorage.setItem('ville', villesList);
}
else{
var oldDate = new Date(localStorage['date']).getTime();;
var currentDate = new Date().getTime();
var distance = currentDate - oldDate ;
var days = Math.floor(distance / (1000 * 60 * 60 * 24));
if (days >= 15){
localStorage.setItem('date',date);
localStorage.setItem('ville', villesList);
}
}
优势:
- 业务逻辑 (
dao.listeVilles()
) 只调用一次(但不能保证...) - 潜在的巨大列表只发送一次到浏览器,减少了数据传输开销。
无论如何,遵循 Ricardo 和 Holger 对 Why JSF calls getters multiple times 的建议的建议是一个好主意,建议将您的 bean 更改为:
@ManagedBean (name="villeBean")
@ViewScoped
public class Villes {
private String villesList;
public String listeVilles{
if(villesList == null) {
villesList = dao.listeVilles();
}
return villesList;
}
}
优势:
- 业务逻辑 (
dao.listeVilles()
) 仅在 xhtml 文件中出现任意次数的#{villeBean.listeVilles()}
时调用一次。
风险:
- 在您的 xhtml 中每次出现
#{villeBean.listeVilles()}
仍会呈现和传输潜在的巨大列表。 - 您的视图在使用 AJAX 时可能会看到 old/obsolete 数据,因为这是在同一视图范围内永远不会失效的缓存。
事实证明,您实际上希望在 localStorage
中有一个客户端缓存,并在该客户端缓存有效时阻止业务逻辑调用,您
必须走 AJAX 路:
我建议实施 javascript 有条件地呈现 仅在需要时更新本地存储:
<h:panelGroup id="updateLocalStorageScript" style="display: none;">
<h:outputScript rendered="#{villeBean.updateLocalStorage}">
localStorage.setItem('date',date);
localStorage.setItem('ville', #{villeBean.villesList});
</h:outputScript>
</h:panelGroup>
可以通过此处建议的命令操作触发更新:How to invoke a JSF managed bean on a HTML DOM event using native JavaScript?
<h:form id="frmHidden" style="display: none;">
<h:commandButton id="cmdDoUpdateLocalStorage" action="#{villeBean.doUpdateLocalStorage()}">
<f:ajax execute="@this" render=":updateLocalStorageScript" />
</h:commandButton>
</h:form>
当然你也可以使用p:remoteCommand
,OmniFaces解决方案或上述QA中提出的其他建议。
此命令操作导致 javascript 被呈现并通过仅调用您的业务逻辑一次来初始化列表值。
@ManagedBean (name="villeBean)"
@ViewScoped
public class Villes {
private boolean updateLocalStorage;
private String villesList;
public void doUpdateLocalStorage() {
updateLocalStorage = true;
villesList = dao.listeVilles();
}
public boolean isUpdateLocalStorage() {
return updateLocalStorage;
}
public String getVillesList() {
return villesList;
}
}
从您的 javascript 条件块中触发该命令:
if(localStorage['ville'] == null || localStorage['date']==null){
document.getElementById('frmHidden:cmdDoUpdateLocalStorage').onclick();
}
else{
var oldDate = new Date(localStorage['date']).getTime();;
var currentDate = new Date().getTime();
var distance = currentDate - oldDate ;
var days = Math.floor(distance / (1000 * 60 * 60 * 24));
if (days >= 15){
document.getElementById('frmHidden:cmdDoUpdateLocalStorage').onclick();
}
}