【问题标题】:How to use attribute value in Javascript in JSF composite component?如何在JSF复合组件中使用Javascript中的属性值?
【发布时间】:2017-09-04 09:50:23
【问题描述】:

对于我的一个 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>

这是按预期工作的。但是,由于我无法直接从 JS 中检索 start 属性值,所以我让 h:outputText 先显示日期值,然后 JS 将从呈现的 HTML 中检索它并用经过的时间替换它。

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

所以我的问题是:有没有更简洁的方法来做到这一点,例如直接访问属性(如果可能的话)?

或者这只是你在复合材料中无法做到的事情?

非常感谢!

【问题讨论】:

  • 首先,这与“复合”无关。其次,“但是,由于我无法直接从JS中检索start属性值”是什么意思,您的解决方案没有错,它是正常的。尝试找出“延迟”的来源。你知道这一点:timeago.yarp.com(无需自己构建)
  • @Kukeltje:谢谢!我的问题是通过属性将计算所基于的时间值带入组件中。因为我无法直接读取它的值,所以我将 outputText 组件绑定到属性,然后从 HTML 中选择值。与此同时,我发现您也可以在 Javascript 中使用 EL - 这是我想要的直接访问,请参阅我的答案。谢谢前段时间提,我去看看!最好的问候,烤面包机

标签: javascript jsf primefaces jsf-2


【解决方案1】:

您可以通过仅在第一次遇到组件时渲染函数来避免 JS 函数冲突。如果您愿意将您的 JS 外部化,您可以在 xhtml 中包含所有内容。请参阅BalusC的这篇帖子

<cc:interface/>

<cc:implementation>
    <h:outputScript target="head" name="js/myJs.js"/>
    <div id="#{cc.clientId}">
        <h:outputText id="output" value="#{cc.attrs.value}" />
        <script>
            update("#{cc.clientId}:output");
            setInterval(update, 1000, "#{cc.clientId}:output");
        </script>
    </div>
</cc:implementation>

或者,您可以使用&lt;cc:interface/&gt;componentType 属性来完成此操作,然后使用@FacesComponent(name=... 注释来注释Java 类。然后,在您的 Java 类中,您可以覆盖 encodeBegin 以将您的 JS 写入响应(如果您还没有的话)。 这是合成的

<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:cc="http://java.sun.com/jsf/composite">

<cc:interface componentType="jsTestComp" />

<cc:implementation>
    <div id="#{cc.clientId}">
        <h:outputText id="output" value="#{cc.attrs.value}" />
        <script>
            update("#{cc.clientId}:output");
            setInterval(update, 1000, "#{cc.clientId}:output");
        </script>
    </div>
</cc:implementation>
</html>

还有 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);
    }
}

【讨论】:

  • 为什么不使用@ResourceDependency注解?这样你就可以完全外部化 js(包括例如 timeago 和 css,如果需要的话......)将你的代码减少 10 行 ;-)
  • @Kukeltje 好点,但如果你想将其外部化,我发现了一种更简洁的方法stackoverflow.com/a/7393741/2780051。我会更新我的答案以包含它。
  • 更好的点...谢谢..!!! (老实说,我会删除“FacesComponent”解决方案或切换它们(后者更好)
【解决方案2】:

由于ForguesR 已经是pointed out,您可以只使用timeago jQuery plugin

我把插件打包成JSF component。只需添加此依赖项:

<dependency>
  <groupId>com.github.jepsar</groupId>
  <artifactId>timeago-jsf</artifactId>
  <version>1.0</version>
</dependency>

这个命名空间:

xmlns:ta="http://jepsar.org/timeago"

现在您可以通过使用value 属性传递java.util.Date 来使用该组件:

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

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

本地化脚本会根据 JSF UIViewRoot#getLocale() 自动加载。

【讨论】:

  • 哇!!像魅力一样工作,非常感谢!这不仅是一个很好的解决我的问题的解决方案,而且我不费吹灰之力,而且也是如何制作自定义 jsf 组件的一个很好的例子。但是,对于有类似问题但用例不同的人,axemois 答案可能会更有帮助,所以我会接受他的答案。不过,您显然已经为这个答案付出了相当多的努力,所以我冒昧地对您的其他一些答案进行了投票。再次感谢!!
【解决方案3】:

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

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

这将输出:9 years ago

就像 axemoi 建议的那样,将值作为参数传递。类似的东西:

Last modified : <time class="loaded timeago" datetime="#{YourBean.lastEdited}">#{YourBean.lastEdited}</time>

【讨论】:

  • “传递”是(部分)问题;-)。 编辑:AHH​​HH 现在我看到timeago 正在寻找&lt;time&gt; 标签...好的,...
  • 这看起来像是一个很好的 JSF 组件。刚刚完成并上传到 Sonatype。 github.com/jepsar/Timeago-JSF-Component 。随意在 GitHub 上做出贡献。
  • @JasperdeVries 太棒了!我可能最终也会使用它!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-08-25
  • 2012-10-09
  • 2017-11-11
  • 2012-04-09
  • 2011-02-24
相关资源
最近更新 更多