多语言字符串对象的 JSF 复合组件
Posted
技术标签:
【中文标题】多语言字符串对象的 JSF 复合组件【英文标题】:JSF Composite Component for MultilingualString object 【发布时间】:2014-07-22 10:52:37 【问题描述】:我正在编写一个需要国际化的 JSF 应用程序。为此,我创建了一个 MultilingualString :
public class MultilingualString
/* The Language class is basically a wrapper for a java.util.Locale */
private Map<Language, String> strings;
/* business methods, getters, setters */
现在,有多个表单需要填充 MultilingualString,每次我需要将这样的对象放入表单时重复 c:forEach 循环是非常难看的。所以我听说了 JSF 复合组件,并尝试为此编写一个。
这是我的 inputMultilingualString.xhtml :
<ui:component xmlns:h="http://xmlns.jcp.org/jsf/html"
xmlns:composite="http://xmlns.jcp.org/jsf/composite"
xmlns:f="http://xmlns.jcp.org/jsf/core"
xmlns:ui="http://xmlns.jcp.org/jsf/facelets">
<composite:interface componentType="inputMultilingualString">
<composite:attribute name="value" required="true"
type="com.tob.entities.internationalization.MultilingualString"/>
<composite:attribute name="languages" type="java.util.List" default="#null"/>
</composite:interface>
<composite:implementation>
<f:event type="preRenderComponent" listener="#cc.init"/>
<h:dataTable id="#cc.clientId" value="#cc.languages" var="language">
<h:column>
<h:outputLabel value="#language"/>
</h:column>
<h:column>
<h:inputText binding="#cc.inputs[language]"/>
</h:column>
</h:dataTable>
</composite:implementation>
</ui:component>
所以我希望 value 属性是 MultilingualString 的一个实例,而 language 属性是语言列表的一个实例。 如果语言属性为空,我希望复合组件在 dataTable 中为 MultilingualString 中包含的地图中的每个条目显示一行。
现在这是我在 InputMultilingualString.java 中的“支持组件”:
@FacesComponent(value = "inputMultilingualString", createTag = true)
public class InputMultilingualString extends UIInput implements NamingContainer
private final Map<Language, UIInput> inputs = new HashMap();
private List<Language> languages;
@Override
public String getFamily()
return (UINamingContainer.COMPONENT_FAMILY);
public void init()
List<Language> ls = (List<Language>) this.getAttributes().get("languages");
MultilingualString ms = (MultilingualString) this.getValue();
/* Setting languages */
if (ls != null)
this.setLanguages(ls);
else
this.languages = new ArrayList();
this.languages.addAll(ms.getStrings().keySet());
/* Initializing inputs */
UIInput tmp;
for (Language l : this.languages)
tmp = new UIInput();
tmp.setValue(ms.getString(l));//
this.inputs.put(l, tmp);
@Override
public String getSubmittedValue()
String ret = new String();
for (Map.Entry<Language, UIInput> entry : this.inputs.entrySet())
if (entry.getValue() != null)
if (!ret.isEmpty())
ret += ',';
ret += entry.getKey().getLanguageTag(); // NullPointerException here when the form is submitted
ret += "=" + entry.getValue().getSubmittedValue();
return (ret);
@Override
protected Object getConvertedValue(FacesContext context, Object submittedValue)
MultilingualString ms = (MultilingualString) this.getValue();
String[] entries = ((String) submittedValue).split(",");
String[] pair;
Language language;
for (String entry : entries)
pair = entry.split("=");
language = new Language();
language.setLanguageTag(pair[0]);
ms.addString(language, pair[1]);
return (ms);
public List<Language> getLanguages()
return (this.languages);
public void setLanguages(List<Language> languages)
this.languages = languages;
public Map<Language, UIInput> getInputs()
return (this.inputs);
为了实现我想要显示输入的语言的规则,我向支持组件添加了一个语言属性,并在 preRenderComponent 事件上调用的 init 方法中对其进行了初始化。 语言列表已正确初始化。
这是我使用复合组件的方式:
<ui:composition template="/Templates/Common.xhtml"
xmlns="http://www.w3.org/1999/xhtml"
xmlns:h="http://xmlns.jcp.org/jsf/html"
xmlns:ui="http://xmlns.jcp.org/jsf/facelets"
xmlns:tob="http://xmlns.jcp.org/jsf/composite/components"
xmlns:p="http://primefaces.org/ui">
<ui:define name="content">
<h:form id="testForm">
<tob:inputMultilingualString value="#testBean.ms" languages="#testBean.languages"/>
<!-- The testBean.ms contains :
[English] => string-English
[français] => string-français
[русский] => string-русский
And the testBean.languages contains a list of Language objects for English, French, and Russian -->
<p:commandButton value="Submit" action="#testBean.submit()"/>
</h:form>
</ui:define>
</ui:composition>
问题是:
如果作为值输入的 MultilingualString 已经包含一些字符串,它们不会显示在 inputText 中,因为如果您填写 inputText 值属性(我阅读了article of BalusC on the topic,他不需要填写值属性使他的下拉列表具有正确的值)。我在 *** 上的某处读到了 BalusC 的答案,如果 inputText 的绑定属性中引用的 UIInput 被评估为 null,则创建它,这就是为什么我尝试在 init 方法中初始化它们,但到目前为止没有运气。 当我提交表单时,我在 getSubmittedValue() 方法中的 getKey() 调用中收到 NullPointerException。这怎么可能?我希望这很清楚,有人可以帮助我! 谢谢!
编辑: 我正在使用 GlassFish 4,并手动将 Mojarra 更新为 2.2.6
【问题讨论】:
整个页面根本不应该显示,但在页面加载期间已经失败,binding
出现 EL 异常。在视图构建期间,您不能在 null
的渲染时间变量上使用 binding
。您真的在代码中使用了<h:dataTable>
吗?这实际上不是<c:forEach>
吗?我可以指出回发期间描述的失败的实际错误,但是给定的代码在显示期间已经失败,所以我现在很困惑。
@BalusC 我复制粘贴了我在问题中编写的代码,并且页面确实显示了。所以我知道这里不可能使用<h:dataTable>
。但是<c:forEach>
的问题不会相同吗?我仍然无法初始化我的Map<Language, UIInput>
。
什么 JSF impl/version?它在 Mojarra 2.2.6 上失败。但无论如何,要求是理解的。这一切都是可能的,不需要支持组件,我会给出答案。
@BalusC 就是这个:Mojarra 2.2.6。 GlassFish 开始输出样本:Initialisation de Mojarra 2.2.6 ( 20140304-1537 https://svn.java.net/svn/mojarra~svn/tags/2.2.6@12949)
【参考方案1】:
目前发布的代码存在2个技术问题:
您正在对仅在视图渲染期间可用的变量使用binding
。 binding
属性在视图构建期间运行,而不是在视图渲染期间运行。在这种特殊情况下,当binding
被执行时,#language
是null
。另见How does the 'binding' attribute work in JSF? When and how should it be used?。此外,您似乎期望会生成多个 <h:inputText>
组件,但事实并非如此。只有一个在渲染视图期间被多次重用。只有当您使用<c:forEach>
而不是<h:dataTable>
时,才会在物理上生成多个<h:inputText>
组件。另见JSTL in JSF2 Facelets... makes sense?
您没有为回发保存组件的状态。您应该删除languages
属性并让getter 和setter 委托给getStateHelper()
。另见How to save state when extending UIComponentBase。
但是,整体方法很笨拙。您不需要功能需求的支持组件。只需在MultilingualString
中添加一个List<Languages>
getter 并将其直接用作languages
属性的default
。
所以,如果你把这个添加到MultilingualString
:
public List<Language> getLanguages()
return new ArrayList<>(strings.keySet());
然后通过#cc.attrs
引用属性:
<cc:interface>
<cc:attribute name="value" required="true" type="com.tob.entities.internationalization.MultilingualString"/>
<cc:attribute name="languages" type="java.util.List" default="#cc.attrs.value.languages" />
</cc:interface>
<cc:implementation>
<h:dataTable value="#cc.attrs.languages" var="language">
<h:column>
<h:outputLabel value="#language"/>
</h:column>
<h:column>
<h:inputText value="#cc.attrs.value.strings[language]" />
</h:column>
</h:dataTable>
</cc:implementation>
然后它应该按预期工作。请注意使用大括号符号[]
引用动态映射键的可能性。这可能是您解决方案的全部关键(您似乎没有意识到这一点,因此正在努力寻求过于复杂的解决方案)。
【讨论】:
确实,看到您的回答,最初的方法显然过于复杂。感谢您的回答和MultilingualString.getLanguages()
技巧!以上是关于多语言字符串对象的 JSF 复合组件的主要内容,如果未能解决你的问题,请参考以下文章