Я пишу приложение JSF, которое должно быть интернационализировано. Для этого я создал MultilingualString:Компонентный компонент 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, и это довольно некрасиво повторить переменный ток: Foreach цикла каждый раз, когда мне нужно поставить такой объект в форма. Поэтому я слышал о составных компонентах JSF Composite, и я попытался написать их для этой цели.
Вот мой 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>
Так что я хочу, чтобы значения атрибута, чтобы быть экземпляром MultilingualString и языки приписывают быть экземпляром списка языка. Если атрибут languages имеет значение null, я хочу, чтобы композитный компонент отображал строку в 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);
}
}
В целях реализации правила на каких языках я хочу, чтобы отобразить вход, я добавил языки атрибутов к компоненту основы и инициализировал его в методе init, который вызывается в событии preRenderComponent. Список языков правильно инициализирован.
Вот как я использую мой составной компонент:
<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 как это если вы заполните атрибут value inputText (я прочитал article of BalusC on the topic, и ему не нужно заполнять атрибут value, чтобы его выпадающие списки имели правильное значение). Я прочитал ответ BalusC где-то в stackoverflow, что UIInput, на который ссылается атрибут привязки inputText, создается, если его оценивают как null, поэтому я попытался инициализировать их в методе init, но пока не повезло.
- Когда я отправляю форму, я получаю исключение NullPointerException при вызове getKey() в методе getSubmittedValue(). Как это возможно?
Надеюсь, это было ясно и что кто-то может мне помочь! Спасибо!
Edit: Я использую GlassFish 4 и я вручную обновил Mojarra на 2.2.6
Вся страница не должна отображаться вообще, но уже не выполняется во время загрузки страницы с исключением EL на 'binding'. Вы не можете использовать 'binding' в переменной времени рендеринга, которая является' null' во время времени сборки. Вы действительно использовали '' в своем коде? Разве это не ''? Я могу указать на фактическую ошибку в отношении описанного сбоя во время обратной передачи, но данный код уже не работает во время отображения, поэтому я сейчас запутался. –
BalusC
@BalusC Я скопировал код, который я написал в вопросе, и страница отображается. Поэтому я понимаю, что здесь невозможно использовать ''. Но не будет ли проблема такой же: ''? Я все равно не смогу инициализировать мой «Map ». –
Unda
Что такое JSF impl или версия? Он терпит неудачу в Мохарре 2.2.6. Но в любом случае это требование понимается. Это все возможно без необходимости в компоненте поддержки, я буду испечь ответ. – BalusC