Составной компонент JSF - странное поведение при попытке сохранить состояние
Я использую Glassfish 3.2.2 и JSF 2.1.11
Я пытаюсь создать составной компонент, который будет принимать в качестве параметров строку и максимальное количество символов, а затем будет отображать только максимальное количество символов, но рядом с ним будет ссылка "еще", которая при нажатии будет разверните текст до полной длины, и рядом с ним будет ссылка "меньше", чтобы вернуть его к максимальному количеству символов.
Я вижу странное поведение, поэтому мне интересно, делаю ли я что-то не так.
Вот мое определение составного компонента:
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:composite="http://java.sun.com/jsf/composite"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:fn="http://java.sun.com/jsp/jstl/functions"
xmlns:p="http://primefaces.org/ui">
<composite:interface componentType="expandableTextComponent">
<composite:attribute name="name" required="true"/>
<composite:attribute name="maxCharacters" required="true"/>
<composite:attribute name="value" required="true"/>
</composite:interface>
<composite:implementation>
<h:panelGroup id="#{cc.attrs.name}">
<h:outputText value="#{fn:substring(cc.attrs.value, 0, cc.attrs.maxCharacters)}" rendered="#{fn:length(cc.attrs.value) le cc.attrs.maxCharacters}"/>
<h:outputText value="#{fn:substring(cc.attrs.value, 0, cc.attrs.maxCharacters)}" rendered="#{fn:length(cc.attrs.value) gt cc.attrs.maxCharacters and !cc.expanded}" style="margin-right: 5px;"/>
<h:outputText value="#{cc.attrs.value}" rendered="#{fn:length(cc.attrs.value) gt cc.attrs.maxCharacters and cc.expanded}" style="margin-right: 5px;"/>
<p:commandLink actionListener="#{cc.toggleExpanded()}" rendered="#{fn:length(cc.attrs.value) gt cc.attrs.maxCharacters}" update="#{cc.attrs.name}">
<h:outputText value="#{__commonButton.more}..." rendered="#{!cc.expanded}"/>
<h:outputText value="#{__commonButton.less}" rendered="#{cc.expanded}"/>
</p:commandLink>
</h:panelGroup>
</composite:implementation>
</html>
А вот и компонент Java:
@FacesComponent("expandableTextComponent")
public class ExpandableTextComponent extends UINamingContainer
{
boolean expanded;
public boolean isExpanded()
{
return expanded;
}
public void toggleExpanded()
{
expanded = !expanded;
}
}
К сожалению, расширение всегда ложно каждый раз, когда вызывается функция toggleExpanded.
Однако, если я изменю составной компонент на следующий, тогда он будет работать.
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:composite="http://java.sun.com/jsf/composite"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:fn="http://java.sun.com/jsp/jstl/functions"
xmlns:p="http://primefaces.org/ui">
<composite:interface componentType="expandableTextComponent">
<composite:attribute name="name" required="true"/>
<composite:attribute name="maxCharacters" required="true"/>
<composite:attribute name="value" required="true"/>
</composite:interface>
<composite:implementation>
<h:panelGroup id="#{cc.attrs.name}">
<h:outputText value="#{fn:substring(cc.attrs.value, 0, cc.attrs.maxCharacters)}" rendered="#{fn:length(cc.attrs.value) le cc.attrs.maxCharacters}"/>
<h:outputText value="#{fn:substring(cc.attrs.value, 0, cc.attrs.maxCharacters)}" rendered="#{fn:length(cc.attrs.value) gt cc.attrs.maxCharacters and !cc.expanded}" style="margin-right: 5px;"/>
<p:commandLink actionListener="#{cc.toggleExpanded()}" rendered="#{fn:length(cc.attrs.value) gt cc.attrs.maxCharacters and !cc.expanded}" update="#{cc.attrs.name}" process="@this">
<h:outputText value="#{__commonButton.more}..."/>
</p:commandLink>
<h:outputText value="#{cc.attrs.value}" rendered="#{fn:length(cc.attrs.value) gt cc.attrs.maxCharacters and cc.expanded}" style="margin-right: 5px;"/>
<p:commandLink actionListener="#{cc.toggleExpanded()}" rendered="#{fn:length(cc.attrs.value) gt cc.attrs.maxCharacters and cc.expanded}" update="#{cc.attrs.name}" process="@this">
<h:outputText value="#{__commonButton.less}"/>
</p:commandLink>
</h:panelGroup>
</composite:implementation>
</html>
Если я помещаю точку останова в функцию toggleExpanded, она вызывается только по ссылке "больше", а не по ссылке "меньше". Итак, вопрос в том, почему он не вызывается, когда я нажимаю на ссылку "меньше"? Разве этот код не должен быть эквивалентен коду выше?
Есть ли лучший способ сохранить состояние в компоненте?
1 ответ
По сути, вы должны быть переопределены UIComponent#saveState()
а также restoreState()
в соответствии с их документацией, когда вы добавляете пользовательские свойства / атрибуты к компоненту, которые должны сохраняться при нескольких HTTP-запросах в одном представлении. Экземпляр компонента создается заново при каждом запросе.
Начиная с JSF 2.0, гораздо лучше использовать StateHelper
непосредственно в атрибуте getters/setters. Это доступно по наследству UIComponent#getStateHelper()
метод.
private enum PropertyKeys {
expanded;
}
public void toggleExpanded() {
setExpanded(!isExpanded());
}
public void setExpanded(boolean expanded) {
getStateHelper().put(PropertyKeys.expanded, expanded);
}
public boolean isExpanded() {
return (boolean) getStateHelper().eval(PropertyKeys.expanded, false);
}