4

私の複合コンポーネントは、PrimeFaces DataTable 内に配置されると、StateHelper に格納された値を共有します。私が見たコンポーネントの状態を維持することに関するほとんどの例はgetStateHelper().put()、. 私はこれらの方法を使用しますが、運がありません。それを適切に行う方法は?(現在、この投稿の最後に記載されている回避策を使用しています)eval()UINamingContainer

この問題を説明するために、PrimeFaces commandLink コンポーネントに基づいてクリック カウンターを作成しました。以下の例では、dataTable の外部にある 2 つのカウンターが期待どおりに機能します。ただし、dataTable 内に表示されるすべてのカウンターは、同じカウンター値を共有します (それらのいずれかをクリックすると、共通の値が続きます)。

更新: データ テーブル内で並べ替え (たとえば) を正しく機能させるには、コンポーネントを何らかの方法で特定の raw にバインドする必要があることがわかりました。そして、「共有」状態ヘルパーはまさにそれを可能にします。そのため、行キーを属性として指定し、状態を格納するメソッドを更新しました。この方法が正しいかどうかは疑問の余地がありません。

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

元の例:

プライムフェイス 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