如何在JSF复合组件中使用Javascript中的属性值?

如何在JSF复合组件中使用Javascript中的属性值?,javascript,jsf,primefaces,jsf-2,Javascript,Jsf,Primefaces,Jsf 2,对于我的一个JSF/primefaces项目,我想显示从给定日期起经过的时间——想想“这个条目最近一次编辑是在3小时5米前” 首先,我计算了支持bean中的时间间隔,并让视图对其进行轮询。这意味着每秒一次ajax调用,而且很容易中断—这不好 因此,我为该任务制作了第一个简单的JSF复合组件。基本上,它只是h:outputText的包装器:它将开始日期作为属性,然后在Javascript中,它每秒计算到当前日期的时间间隔,并相应地更新outputText 代码如下: <?xml versio

对于我的一个JSF/primefaces项目,我想显示从给定日期起经过的时间——想想“这个条目最近一次编辑是在3小时5米前”

首先,我计算了支持bean中的时间间隔,并让视图对其进行轮询。这意味着每秒一次ajax调用,而且很容易中断—这不好

因此,我为该任务制作了第一个简单的JSF复合组件。基本上,它只是h:outputText的包装器:它将开始日期作为属性,然后在Javascript中,它每秒计算到当前日期的时间间隔,并相应地更新outputText

代码如下:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:h="http://java.sun.com/jsf/html"
      xmlns:f="http://java.sun.com/jsf/core"
      xmlns:composite="http://java.sun.com/jsf/composite">

    <composite:interface>

        <composite:attribute name="start" />

    </composite:interface>

    <composite:implementation>

        <div id="#{cc.clientId}" class="elapsedTime">
            <h:outputText id="outTxt" value="#{cc.attrs.start}" />
        </div>

        <script type="text/javascript">
            var outTxt = document.getElementById("#{cc.clientId}:outTxt");
            var a = outTxt.innerHTML.split(/[^0-9]/);
            var baseDate = new Date(a[0], a[1] - 1, a[2], a[3], a[4], a[5]);

            function timeStringFromSeconds(s)
            {
                var hours = Math.floor((s / 86400) * 24);
                var minutes = Math.floor(((s / 3600) % 1) * 60);
                var seconds = Math.round(((s / 60) % 1) * 60);

                if (minutes &lt; 1) {
                    minutes = "00";
                } else if (minutes &lt; 10) {
                    minutes = "0" + minutes;
                }

                if (seconds &lt; 1) {
                    seconds = "00";
                } else if (seconds &lt; 10) {
                    seconds = "0" + minutes;
                }

                return(hours + ":" + minutes + ":" + seconds);
            }

            function update() {
                var currentDate = new Date();
                var elapsed = (currentDate - baseDate) / 1000;
                outTxt.innerHTML = timeStringFromSeconds(elapsed);
            }

            update();
            setInterval(update, 1000);
        </script>

    </composite:implementation>

</html>

var outTxt=document.getElementById(“#{cc.clientId}:outTxt”);
var a=outTxt.innerHTML.split(/[^0-9]/);
var baseDate=新日期(a[0],a[1]-1,a[2],a[3],a[4],a[5]);
函数timeStringFromSeconds(s)
{
var小时=数学楼层((s/86400)*24);
var分钟=数学地板((s/3600)%1)*60);
变量秒数=数学轮((s/60)%1)*60);
若有(第1分钟){
分钟=“00”;
}如果有的话(10分钟){
分钟=“0”+分钟;
}
如果(秒1){
秒数=“00”;
}否则,如果(10秒){
秒=“0”+分钟;
}
返回时间(小时+“:“+分钟+”:“+秒);
}
函数更新(){
var currentDate=新日期();
var经过=(currentDate-baseDate)/1000;
outTxt.innerHTML=timeStringFromSeconds(已用);
}
更新();
设置间隔(更新,1000);
这是预期的工作。但是,由于我无法直接从JS检索start属性值,因此我让h:outputText首先显示日期值,然后JS将从呈现的HTML检索该值,并将其替换为经过的时间

因此,尽管我会立即更新该值,但在某些浏览器/设备上,原始日期值会短暂可见。整个事情对我来说就像是一个丑陋的解决办法。如果出于某种原因,我想使用第二个属性,那么我根本无法使用它,因此这种方法显然是有限的/失败的

所以我的问题是:有没有更干净的方法来实现这一点,比如直接访问属性(如果可能的话)

或者这仅仅是你在合成中做不到的事情


非常感谢

事实上,这不是JSF的问题。您只需要使用JSF检索初始值并将其传递给脚本。与其重新发明轮子,不如看一看。以下是演示页面中的一个简单示例:

<time class="timeago" datetime="2008-07-17T09:24:17Z">July 17, 2008</time>

通过仅在第一次遇到组件时渲染函数,可以避免JS函数冲突。如果您愿意将JS外部化,那么可以在xhtml中包含所有内容。请看下面的文章

还有Java代码

@FacesComponent(value="jsTestComp")
public class JSTestComp extends UINamingContainer {
    private static final String JS_RENDERED_KEY = "JSTestComp.jsRendered";
    private static final String js = 
        "function update(elementId){\n" +
        "    var outTxt = document.getElementById(elementId);\n" +                  
        "    outTxt.innerHTML = outTxt.innerHTML + \"more\";\n" +
        "}\n";

    @Override
    public void encodeBegin(FacesContext context) throws IOException {
        Map<String, Object> reqMap = FacesContext.getCurrentInstance()
                .getExternalContext().getRequestMap();

        if(reqMap.get(JS_RENDERED_KEY) == null){
            reqMap.put(JS_RENDERED_KEY, Boolean.TRUE);
            ResponseWriter out = FacesContext.getCurrentInstance().getResponseWriter();
            out.startElement("script", null);
            out.write(js);
            out.endElement("script");
        }
        super.encodeBegin(context);
    }
}
@FacesComponent(value=“jsTestComp”)
公共类JSTestComp扩展了UINamingContainer{
私有静态最终字符串JS_RENDERED_KEY=“JSTestComp.jsRendered”;
私有静态最终字符串js=
“函数更新(elementId){\n”+
“var outTxt=document.getElementById(elementId);\n”+
“outTxt.innerHTML=outTxt.innerHTML+\“更多”;\n”+
“}\n”;
@凌驾
public void encodeBegin(FacesContext上下文)引发IOException{
Map reqMap=FacesContext.getCurrentInstance()
.getExternalContext().getRequestMap();
if(reqMap.get(JS_渲染_键)==null){
reqMap.put(JS_呈现_键,Boolean.TRUE);
ResponseWriter out=FacesContext.getCurrentInstance().getResponseWriter();
out.startElement(“脚本”,空);
out.write(js);
out.endElement(“脚本”);
}
super.encodeBegin(上下文);
}
}
如前所述,您可以使用

我拿了插件,把它包装成一个文件。只需添加此依赖项:

<dependency>
  <groupId>com.github.jepsar</groupId>
  <artifactId>timeago-jsf</artifactId>
  <version>1.0</version>
</dependency>
现在,您可以通过使用
value
属性传递
java.util.Date
来使用组件:

<ta:timeAgo value="#{bean.date}" />

jQuery是从PrimeFaces、BootsFaces或timeago组件加载的


本地化脚本是基于JSF
UIViewRoot#getLocale()
自动加载的。首先,这与“复合”无关。其次,您所说的“然而,由于我无法直接从JS检索开始属性值”,这是什么意思?您的解决方案没有错,这是通常的做法。试着找出“延迟”是从哪里来的。你知道的:(不需要自己建造)@Kukeltje:谢谢!我的问题是,计算经过的时间所依据的值是通过一个属性带入组件的。因为我无法直接读取它的值,所以我将outputText组件绑定到属性,然后从HTML中选取值。与此同时,我发现您也可以在Javascript中使用EL——这是我想要的直接访问,请参阅我的答案。谢谢你刚才提到,我来看看!敬请原谅,为什么不使用
@ResourceDependency
注释呢?通过这种方式,您可以将js完全外部化(如需要,包括timeago和css,并且……)将代码减少10行;-)@Kukeltje的观点很好,但如果您想将其外部化,我发现了一种更干净的方法。我会更新我的答案,包括这一点。更好的一点是。。。谢谢(老实说,我会删除“FacesComponent”解决方案或将其切换(后者更好),“传递”是问题的一部分;-)。编辑:啊,现在我看到了很久以前的样子
@FacesComponent(value="jsTestComp")
public class JSTestComp extends UINamingContainer {
    private static final String JS_RENDERED_KEY = "JSTestComp.jsRendered";
    private static final String js = 
        "function update(elementId){\n" +
        "    var outTxt = document.getElementById(elementId);\n" +                  
        "    outTxt.innerHTML = outTxt.innerHTML + \"more\";\n" +
        "}\n";

    @Override
    public void encodeBegin(FacesContext context) throws IOException {
        Map<String, Object> reqMap = FacesContext.getCurrentInstance()
                .getExternalContext().getRequestMap();

        if(reqMap.get(JS_RENDERED_KEY) == null){
            reqMap.put(JS_RENDERED_KEY, Boolean.TRUE);
            ResponseWriter out = FacesContext.getCurrentInstance().getResponseWriter();
            out.startElement("script", null);
            out.write(js);
            out.endElement("script");
        }
        super.encodeBegin(context);
    }
}
<dependency>
  <groupId>com.github.jepsar</groupId>
  <artifactId>timeago-jsf</artifactId>
  <version>1.0</version>
</dependency>
xmlns:ta="http://jepsar.org/timeago"
<ta:timeAgo value="#{bean.date}" />