JSF composite component - weird behavior when trying to save state

◇◆丶佛笑我妖孽 提交于 2019-12-18 07:05:25

问题


I'm using Glassfish 3.2.2 and JSF 2.1.11

I'm trying to create a composite component that will take as parameters a string and a max number of characters and then will show only the max amount of characters, but it will have a "more" link next to it, that when clicked will expand the text to the full length and will then have a "less" link next to it to take it back to the max number of characters.

I'm seeing some weird behavior, so I'm wondering if I'm doing something wrong.

Here is my composite component definition:

<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>

And here is the Java component:

@FacesComponent("expandableTextComponent")
public class ExpandableTextComponent extends UINamingContainer
{
    boolean expanded;

    public boolean isExpanded()
    {
        return expanded;
    }

    public void toggleExpanded()
    {
        expanded = !expanded;
    }
}

Unfortunately expanded is always false every time the toggleExpanded function is called.

However if I change the composite component to the following then it works.

<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>

If I place a breakpoint in the toggleExpanded function, it only gets called on the "more" link and not the "less" link. So the question is why doesn't it get called when I click on the "less" link? Shouldn't this code be equivalent to the code above?

Is there a better way to save state in a component?


回答1:


Basically, you should be overridding UIComponent#saveState() and restoreState() according their documentation when you add custom properties/attributes to the component which should survive across multiple HTTP requests on the same view. The component instance is namely recreated on every request.

Since JSF 2.0, much better is to use the StateHelper directly in the attribute getters/setters. It's available by the inherited UIComponent#getStateHelper() method.

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);
}


来源:https://stackoverflow.com/questions/13015942/jsf-composite-component-weird-behavior-when-trying-to-save-state

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!