4

当放置在 PrimeFaces DataTable 中时,我的复合组件共享存储在 StateHelper 中的值。我见过的关于保持组件状态的大多数示例都建议使用getStateHelper().put(). 我确实使用这些方法,但没有运气。如何正确地做到这一点?(目前我使用本文末尾描述的解决方法)eval()UINamingContainer

为了说明这个问题,我创建了基于 PrimeFaces commandLink 组件的点击计数器。在下面的示例中,dataTable 之外的两个计数器按预期工作。但是出现在 dataTable 中的所有计数器都共享相同的计数器值(单击其中任何一个都会继续公共值)。

更新: 我发现为了让排序(例如)在数据表中正常工作,我需要以某种方式将我的组件绑定到某些原始数据。而“共享”状态助手允许这样做。所以现在我将行键指定为属性,并更新了存储状态的方法。毫无疑问,这种方式是否正确。

counterLink.xhtml 的更新:

<composite:interface componentType="CounterLink2Component">
    <composite:attribute name="key" type="java.io.Serializable"/>
</composite:interface>

CounterLinkComponent.java 现在是:

@FacesComponent("CounterLinkComponent")
public class CounterLinkComponent extends UINamingContainer {

    private enum PropertyKeys {
        COUNTER_VALUE
    }

    public void count() {
        storeInstanceValue(PropertyKeys.COUNTER_VALUE.toString(), getCounterValue() + 1);
    }

    public Integer getCounterValue(){
        return (Integer) evalInstanceValue(PropertyKeys.COUNTER_VALUE.toString(), 0);
    }

    private Serializable getKeyAttr() {
        return (Serializable) getAttributes().get("key");
    }

    private void storeInstanceValue(String key, Object value) {
        Serializable subkey = getKeyAttr();
        if (subkey == null) {
            getStateHelper().put(key, value);
        }  else {
            getStateHelper().put(subkey, key, value);
        }
    }

    private Object getInstanceValue(String key) {
        Serializable subkey = getKeyAttr();
        if (subkey == null) {
            return getStateHelper().eval(key);
        } else {
            return ((Map) getStateHelper().eval(subkey, Collections.emptyMap())).get(key);
        }
    }

    private Object evalInstanceValue(String key, Object _default) {
        Object result = getInstanceValue(key);
        return result != null ? result : _default;
    }
}

原始示例:

Primefaces 5.0,Glassfish 4。

counterLink.xhtml:

<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:h="http://java.sun.com/jsf/html"
      xmlns:composite="http://java.sun.com/jsf/composite"
      xmlns:p="http://primefaces.org/ui">

<composite:interface componentType="CounterLinkComponent">
</composite:interface>
<composite:implementation>
    <p:commandLink action="#{cc.count()}" partialSubmit="true" update="@this">
        <h:outputText value="#{cc.counterValue}"/>
    </p:commandLink>
</composite:implementation>
</html>

CounterLinkComponent.java:

import javax.faces.component.FacesComponent;
import javax.faces.component.UINamingContainer;
import java.io.Serializable;

@FacesComponent("CounterLinkComponent")
public class CounterLinkComponent extends UINamingContainer {

    private enum PropertyKeys {
        COUNTER_VALUE
    }

    public void count() {
        getStateHelper().put(PropertyKeys.COUNTER_VALUE, getCounterValue() + 1);
    }

    public Integer getCounterValue(){
        return (Integer) getStateHelper().eval(PropertyKeys.COUNTER_VALUE, 0);
    }
}

使用示例:

<h:form>
    <p:panelGrid columns="1">
        <cmp:counterLink/>
        <cmp:counterLink/>

        <p:dataTable var="item" value="#{counterLinkStoreBean.itemList}">
            <p:column headerText="Name">
                #{item.name}
            </p:column>

            <p:column headerText="Counter">
                <cmp:counterLink/>
            </p:column>
        </p:dataTable>
    </p:panelGrid>
</h:form>

此示例的支持 bean(仅创建几个项目):

@Named
@ViewScoped
public class CounterLinkStoreBean implements Serializable {

    private List<Item> itemList;

    @PostConstruct
    private void init() {
        itemList = new ArrayList<Item>();
        itemList.add(new Item("Test 1"));
        itemList.add(new Item("Test 2"));
        itemList.add(new Item("Test 3"));
    }

    public List<Item> getItemList() {
        return itemList;
    }

    public static class Item {
        private final String name;

        public Item(String name) {
            this.name = name;
        }

        public String getName() {
            return name;
        }
    }
}

在我的情况下,我可以使用解决方法将值存储在以组件 clientId 作为辅助键的映射中:

private void storeInstanceValue(Serializable key, Object value) {
    getStateHelper().put(key, getClientId(), value);
}

private Object getInstanceValue(Serializable key) {
    return ((Map)getStateHelper().eval(key, Collections.emptyMap())).get(getClientId());
}

有更自然的解决方案吗?

4

0 回答 0