【问题标题】:How to call JSF backing bean method using javascript only when The page is loaded仅在加载页面时如何使用javascript调用JSF支持bean方法
【发布时间】:2019-10-03 14:28:25
【问题描述】:

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

我已经阅读了@BalusC 的这个答案

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

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 次​​p>

【问题讨论】:

  • @Ricardo 我的 java 脚本代码调用了方法,问题是在生成 HTML 输出期间,所有以 ${} 和 #{} 形式的 EL 表达式都将在服务器端执行。所以后面的方法执行2次就像java脚本条件“不起作用”
  • 这是不正确的,您的 javascript 代码没有调用 bean。渲染页面(包括 javascript 代码)时,javascript 会填充 bean 调用的返回值。 listVilles() 的值将与呈现的页面一起传递。如果您不喜欢多次调用,请执行 if(value==null) value=dao.listVilles(); return value; 之类的操作

标签: javascript jsf el


【解决方案1】:

事实证明,您实际上希望在localStorage 中有一个客户端缓存,并在该客户端缓存有效时阻止业务逻辑调用 必须走AJAX方式:

我建议实现一个 有条件渲染,它只在需要时更新本地存储:

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

【讨论】:

    【解决方案2】:

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

    您可以将该 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()");
    

    【讨论】:

      【解决方案3】:

      您的 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()) 仅调用一次(但不能保证...)
      • 潜在的巨大列表只发送到浏览器一次,减少了数据传输开销。

      Why JSF calls getters multiple times 的答案中听从 Ricardo 和 Holger 的建议,建议将您的 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 时,您的视图可能会看到旧的/过时的数据,因为这是在同一视图范围内永远不会失效的缓存。

      【讨论】:

      • 是的,它只会被调用一次,但是当 localStrorage('date') 和 localStrorage('ville') 不为空时,我根本不想调用它,你知道我的意思吗?跨度>
      • @MohammedABOUZID 所以你的问题通常是如何在客户端 localStorage 中缓存 JSF 服务器端数据?
      猜你喜欢
      • 2012-08-03
      • 2023-03-23
      • 1970-01-01
      • 2011-04-04
      • 1970-01-01
      • 2015-01-10
      • 2014-02-25
      • 2018-05-21
      • 2011-09-14
      相关资源
      最近更新 更多