如何仅在加载页面时使用 javascript 调用 JSF 支持Bean方法



问题是后面的Bean Methode被调用了很多次

好吧,我已经阅读了这个答案@BalusC

JSF 是一种服务器端语言,它在 HTTP 请求后在 Web 服务器上运行,并生成 HTML/CSS/JS 代码,这些代码与 HTTP 响应一起返回。在生成 HTML 输出期间,所有 ${} 和 #{} 形式的 EL 表达式都将在服务器端执行。JavaScript是一种客户端语言,运行在Web浏览器上,并在HTML DOM树上运行。HTML onclick 属性应该指定一个 JavaScript 函数,该函数将在客户端的特定 HTML DOM 事件上执行。

如何仅在单击/完成/打开时调用 JSF 支持bean方法...事件发生但不在页面加载时发生

所以我明白为什么我的背被方法被调用了很多次,那么我该如何解决这个问题

这是我的背影

@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 次

事实证明,您实际上希望在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>

更新可以通过命令操作触发,如下所示:如何使用本机 JavaScript 在 HTML DOM 事件上调用 JSF 托管 Bean?

<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();
}
} 

你可以使用backbean jsf outputscript 组件来做,当你使用 jsf EL 的时候 javascript 代码里面有多个请求问题是正常的,因为一个是静态的,另一个是动态的。

您可以将该 javascript 代码作为资源/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()");

你的 javascript 中有两个 EL-Expression#{villeBean.listeVilles()}的实例,它们都呈现(并因此被调用)服务器端,而与你的 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 的建议是一个好主意,这些建议来自 为什么 JSF 多次调用 getter 建议将你的 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 时,您的视图可能会看到旧/过时的数据,因为这是在同一视图范围内永远不会失效的缓存。

最新更新