在同一页面上多次使用时,更新自定义ui:组件内的组件

时间:2014-12-01 06:59:43

标签: jsf jsf-2 primefaces custom-component

我正在JSF(Mojarra 2.1.x)中构建一个简单的组件,我必须访问父ui组件才能更新它们,目前我正在使用绑定来实现这一点,但它只有在我没有的情况下才有效在同一页面上多次使用该组件。

所以我需要一个允许我在同一页面上多次使用该组件的解决方案。

在以下代码中,我使用commandButton更新binding="#{msButton}",使用panel更新binding="#{msPanel}"

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<ui:component xmlns="http://www.w3.org/1999/xhtml"
    xmlns:ui="http://java.sun.com/jsf/facelets"
    xmlns:f="http://java.sun.com/jsf/core"
    xmlns:h="http://java.sun.com/jsf/html"
    xmlns:c="http://java.sun.com/jsp/jstl/core"
    xmlns:cc="http://java.sun.com/jsf/composite"
    xmlns:layout="http://sterz.stlrg.gv.at/jsf/layout"
    xmlns:p="http://primefaces.org/ui">

    <cc:interface>
        <cc:attribute name="controller" required="true" />
        <cc:attribute name="converter" required="true" />
    </cc:interface>
    <cc:implementation>
        <p:commandButton id="msButton#{cc.attrs.controller.class.getSimpleName()}" binding="#{msButton}" value="#{msg.mehr} (#{cc.attrs.controller.itemList.size()})" type="button" />
        <p:overlayPanel id="msOverlayPanel" for=":#{msButton.clientId}" hideEffect="fade" my="right top" at="right bottom">
            <p:panel id="msPanel#{cc.attrs.controller.class.getSimpleName()}" binding="#{msPanel}" styleClass="ui-panel-fit">
                <ui:repeat id="repeat" value="#{cc.attrs.controller.itemList}"
                    var="item"> 
                    <p:commandButton id="removeButton"
                        actionListener="#{cc.attrs.controller.removeItem(item)}"
                        icon="ui-icon-trash" update=":#{msPanel.clientId} :#{msButton.clientId}" ajax="true"
                        process="@this" disabled="#{cc.attrs.controller.itemList.size() == 1}"/>
                    <p:selectBooleanButton id="value1" value="#{item.exclude}"
                        offLabel="und" onLabel="und nicht" style="width:80px;">
                        <p:ajax event="change" process="@this" />
                    </p:selectBooleanButton>
                    <p:autoComplete converter="#{cc.attrs.converter}"
                        readonly="#{cc.attrs.readonly}" value="#{item.from}"
                        dropdown="true"
                        completeMethod="#{cc.attrs.controller.autocomplete}" var="gp"
                        itemLabel="#{gp.displayName}" itemValue="#{gp}">
                        <p:ajax event="itemSelect" process="@this" />
                    </p:autoComplete>
                    <h:outputText value=" #{msg.bis} " />
                    <p:autoComplete converter="#{cc.attrs.converter}"
                        readonly="#{cc.attrs.readonly}" value="#{item.to}" dropdown="true"
                        completeMethod="#{cc.attrs.controller.autocomplete}" var="gp"
                        itemLabel="#{gp.displayName}" itemValue="#{gp}">
                        <p:ajax event="itemSelect" process="@this" />
                    </p:autoComplete>
                    <br />
                </ui:repeat>
                <hr />
                <p:commandButton id="addButton" actionListener="#{cc.attrs.controller.addItem}"
                    icon="ui-icon-plus" value="#{msg.zufuegen}" update="@parent :#{msButton.clientId}"
                    ajax="true" process="@this"/>
            </p:panel>
        </p:overlayPanel>
    </cc:implementation>
</ui:component>

任何帮助都非常赞赏。

3 个答案:

答案 0 :(得分:1)

解决方案是使用faces组件bean

@FacesComponent("com.xxx.MultiselectorIdComponent")
public class MultiselectorIdComponent extends UINamingContainer
{

    UIComponent msPanel;

    // getters/settter and other ui compunents    

}

告诉组件接口使用哪个组件bean

<cc:interface componentType="com.xxx.MultiselectorIdComponent">

将JSF组件绑定到faces组件bean中的组件

<p:panel binding="#{cc.msPanel}"/>

并访问组件,例如更新组件,我们使用绑定

<p:commandButton value="My Button" update=":#{cc.msPanel.clientId}"/>

同时 一个好的做法是使用具有以下ID的父容器(如<div>

<div id="#{cc.clientId}">

希望这有帮助, 此致

答案 1 :(得分:0)

您可以按照样式类

定位要更新的组件
<p:commandButton styleClass="msButton" ... />

<p:panel styleClass="msPanel" ... />

我想你会从addButton更新它们,看起来像这样

<p:commandButton id="addButton" update="@(.msButton, .msPanel)" ... />

在同一页面上处理许多cc个实例应该没有问题。

<强>更新

您可以尝试update="@composite",这应刷新整个自定义组件。或者,繁琐的update="@parent @parent:@parent:@parent:@child(1)"分别应该面向面板和按钮。或update="@parent @parent:@parent:@previous",也应该这样做。请查看Primefaces User Guide中的第4.3章,了解更多示例和支持的关键字。

答案 2 :(得分:0)

我通过common bean&#34; bindingBean&#34;解决了类似的问题。存储在ViewContext中。 BindingBean保存组件记录的内部MAP中的所有绑定组件。哈希映射的关键是EL表达式 - 它是组件的工厂。 EL用于创建组件。组件保存在存储在绑定bean中的MAP中的记录中。

示例:

<h:panel binding="#{bindBean.get('msPanelFactory.getPanel()').component}"/>

记录:

public class BindingRecord {
    private Object component;
    private String compExpr;

    public BindingRecord(String compExpr) {
        this.compExpr = compExpr;
    }
    public Object getComponent() {
        if (component == null) {
            component = getValueExprValue("#{" + compExpr + "}");
        }
        return component;
    }
    public void setComponent(Object component) {
        this.component = component;
    }
    public Object getStoredComponent() {
        return component;
    }
}

绑定bean:

public class BindingBean {
    private final Map<String, BindingRecord> bindingMap = new HashMap<>();
    public BindingRecord get(String key) {
        BindingRecord result = bindingMap.get(key);
        if (result == null) {
            result = new BindingRecord(key);
            bindingMap.put(key, result);
        }
        return result;
    }
}

在您的情况下,您可以使用:

<h:panel binding="#{bindingBean.get('panelFactory.create()').component}"/>

在复合组件内部,您可以使用技巧 - 通过复合参数传递组件名称:

<h:panel binding="#{bindingBean.get('panelFactory.create('.concate(cc.attrs.componentName).concate(')')).component}"/>