Составной компонент 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);
}
Другие вопросы по тегам