【问题标题】:Updating components inside a custom ui:component when it is used multiple times on same page在同一页面上多次使用自定义 ui:component 时更新组件
【发布时间】:2015-01-29 03:37:52
【问题描述】:

我正在 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>

非常感谢任何帮助。

【问题讨论】:

    标签: jsf jsf-2 primefaces custom-component


    【解决方案1】:

    您可以通过样式类来定位组件以进行更新

    <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 章,了解更多示例和支持的关键字。

    【讨论】:

    • css 不应该这样使用,也不能解决我无法在一个页面上使用同一组件的多个实例的问题。
    • 样式类作为update 的目标是完全有效且有记录的用法。无论如何,请查看我的更新答案以获取更多建议。
    • 不起作用 javax.faces.FacesException: 找不到标识符为“@parent:@parent”的组件引用自... @composite 也不起作用
    • @lada.dvorak 是的。
    【解决方案2】:

    解决办法是使用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 的父容器(如 &lt;div&gt;

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

    希望这会有所帮助, 问候

    【讨论】:

      【解决方案3】:

      我通过存储在 ViewContext 中的普通 bean“bindingBean”解决了类似的问题。 BindingBean 将所有绑定的组件保存在组件记录的内部 MAP 中。 hash map 的 key 是一个 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}"/>
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2017-06-05
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2021-12-15
        • 1970-01-01
        • 2011-06-04
        • 2015-11-15
        相关资源
        最近更新 更多