如何在 JSF 复合组件中使用 Javascript 中的属性值?
How to use attribute value in Javascript in JSF composite component?
对于我的一个 JSF/primefaces 项目,我想显示自给定日期以来经过的时间 - 想想 "this entry has been last edited 3h 5m ago"。
起初,我在backing 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 < 1) {
minutes = "00";
} else if (minutes < 10) {
minutes = "0" + minutes;
}
if (seconds < 1) {
seconds = "00";
} else if (seconds < 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 中检索它并用经过的时间替换它.
因此,虽然我立即更新了值,但在某些浏览器/设备上,原始日期值会短暂可见。整个事情对我来说就像一个丑陋的解决方法。如果出于某种原因我想使用第二个属性,我将根本无法使用它,因此该方法显然受到限制/损坏。
所以我的问题是:是否有更简洁的方法来执行此操作,例如直接访问属性(如果可能)?
或者这只是您不能在合成中做的事情?
非常感谢!
其实这不是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>
您可以通过仅在第一次遇到组件时渲染函数来避免 JS 函数冲突。如果你愿意将你的 JS 外部化,你可以在你的 xhtml 中包含所有内容。请参阅 BalusC
的 post
<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>
或者,您可以使用 <cc:interface/>
的 componentType
属性完成此操作,然后使用 @FacesComponent(name=...
注释注释 Java Class。在您的 Java Class 中,您可以覆盖 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);
}
}
如ForguesR already , you could just use the 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 组件加载。
基于JSFUIViewRoot#getLocale()
.
自动加载本地化脚本
对于我的一个 JSF/primefaces 项目,我想显示自给定日期以来经过的时间 - 想想 "this entry has been last edited 3h 5m ago"。
起初,我在backing 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 < 1) {
minutes = "00";
} else if (minutes < 10) {
minutes = "0" + minutes;
}
if (seconds < 1) {
seconds = "00";
} else if (seconds < 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 中检索它并用经过的时间替换它.
因此,虽然我立即更新了值,但在某些浏览器/设备上,原始日期值会短暂可见。整个事情对我来说就像一个丑陋的解决方法。如果出于某种原因我想使用第二个属性,我将根本无法使用它,因此该方法显然受到限制/损坏。
所以我的问题是:是否有更简洁的方法来执行此操作,例如直接访问属性(如果可能)?
或者这只是您不能在合成中做的事情?
非常感谢!
其实这不是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>
您可以通过仅在第一次遇到组件时渲染函数来避免 JS 函数冲突。如果你愿意将你的 JS 外部化,你可以在你的 xhtml 中包含所有内容。请参阅 BalusC
的 post<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>
或者,您可以使用 <cc:interface/>
的 componentType
属性完成此操作,然后使用 @FacesComponent(name=...
注释注释 Java Class。在您的 Java Class 中,您可以覆盖 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);
}
}
如ForguesR already
我把插件拿来包装成一个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 组件加载。
基于JSFUIViewRoot#getLocale()
.