JSF:如何避免在视图中找到“组件ID”。复合组件

时间:2017-04-17 04:26:02

标签: jsf jsf-2.3

我知道导致以下问题的原因。我正在寻找一种优雅的方法来解决这个问题,而不是我发现的蛮力(和非DRY)解决方法。

我在模板中有一个高度可重用的h:form(我知道“神形式”的危险,这不是一个神形式),我通过模板将可编辑内容插入到该形式,在页面的顶部和底部都有一个相同的命令按钮操作栏。 (这个模板有数百个客户端,有些客户端插入不同的操作栏。)

我这样做的唯一原因(条形顶部和底部)是用户方便;我发现在使用许多内容管理系统时,必须向下滚动或向上滚动才能找到长表单上的“保存”按钮(或操作栏中的其他按钮)。

模板(请不要告诉我这是“神形”):

<h:form prependId=".." id="form">

 <div id="actions-upper" class="actions">
   <ui:insert name="actions"/>
 </div> 

... other reusable stuff

 <div id="actions-lower" class="actions">
   <ui:insert name="actions"/>
 </div>

</h:form>

作为模板客户端的每个edit.xhtml页面(有很多)都会插入操作栏以及兼容的#{manager}辅助bean参数:

<ui:composition template="/template.xhtml">
...
    <ui:define name="actions">
        <util:edit_actions id="edit_actions" manager="#{manager}"/>
    </ui:define>

 ... other insertions with editable content omitted

注意上面我给出了CC`util:edit_actions'一个id(我直到最近才在这个CC上做了,原因我将在下面解释)。

因此,您可以看到完全相同的操作工具栏插入到页面内部表单部分的顶部和底部。但是,如果您按照edit_actions传递的ID执行此操作,则会获得:

javax.servlet.ServletException: Component ID edit_actions has already been found in the view.  

我已经成功使用这个模板多年,直到我引入了显式id,原因如下所示。

edit_actions CC有一些命令按钮,例如:

 <composite:implementation>
  <p:toolbar>
   <p:toolbarGroup>
    <p:commandButton 
      ajax ="true"
      action="#{cc.attrs.manager.crud.update}"
      value="Save" 
      update="@form"
      id="save"
     />
         ...

现在,常规保存按钮并不总是表单中的唯一按钮;有时其他按钮会使用conditionally required input fields执行临时AJAX操作,例如嵌入式链接表编辑器中的这些操作:

 <p:inputText 
   id="newLinkUrl"
   value="#{cc.attrs.manager.newLinkUrl}"
   required="#{param['edit_actions:save']==null}"
   validator="urlValidator"
 />

<p:commandButton                        
   value="Add new link !"
   action="#{cc.attrs.manager.addNewLink(cc.attrs.element)}"
   update="... newLinkUrl ..."
  />

(其中BT urlValidator没有抛出null,系统依赖于条件required,以便普通@form保存始终有效。)

但要获得必要的条件:

   required="#{param['edit_actions:save']==null}"

每当在使用它的数百 个edit.xhtml客户端页面中执行插入操作时,我必须为插入的edit_actions CC提供显式ID:

<ui:composition template="/template.xhtml">
...
    <ui:define name="actions">
        <util:edit_actions id="edit_actions" manager="#{manager}"/>
    </ui:define>

但是如上所示,如果我在那里包含了id,它现在会导致错误(但没有它我不能使用条件required技巧)。

到目前为止,我找到了两种解决方法:

  1. 根本没有模板中的操作栏两次。这是不可接受的,它只是通过避免它来打破这个功能。

  2. 模板中有2个不同的插入点可行,但您必须小心ID。

  3. 第二个是问题:

    <ui:composition template="/template.xhtml">
     <ui:define name="actions_upper">
      <util:edit_actions id="edit_actions_upper" manager="#{manager}"/>
     </ui:define>
     <ui:define name="actions_lower">
      <util:edit_actions id="edit_actions_lower" manager="#{manager}"/>
     </ui:define>
    

    请注意,上面的代码不是Don't Repeat Yourself (DRY)代码,我认为这是最重要的编码实践之一,而且JSF通常特别擅长解决。确保上面的模板插入模式和id模式在数百个edit.xhtml页面中得到解决只是容易出错,除非我能以某种方式封装ui:define对,同时仍然能够注入任何兼容的{{1 }}

    然后条件要求测试必须在上下保存按钮上进行测试:

    #{manager}

    总而言之,这是一个相当丑陋的非DRY解决方法。

    Q1:有什么方法可以动态地动态更改插入的edit_action.xhtml的id,以便它可以在2个不同的地方出现在模板中,而不会出现冲突的组件ID错误?

    Q2:或者,我是否可以通过某种方式将两个<p:inputText id="newLinkUrl" value="#{cc.attrs.manager.newLinkUrl}" required="#{param['edit_actions_upper:save']==null and param['edit_actions_lower:save']==null}" validator="urlValidator" /> 封装在上部与下部条形插入的变通方法中(如变通方法2中所示),同时仍然能够注入{{ 1}}(这样我可以将它包含在数百个edit.xhtml客户端中作为封装策略重用到模板中)?

    编辑:这种封装我的“双操作条”模式的尝试似乎不起作用。来自ui:define

    #{manager}

    /include/edit_actions_defines.xhtml尝试使用:

    <ui:composition 
        xmlns="http://www.w3.org/1999/xhtml"
        xmlns:f="http://xmlns.jcp.org/jsf/core"
        xmlns:ui="http://xmlns.jcp.org/jsf/facelets"
        xmlns:util="http://xmlns.jcp.org/jsf/composite/util">
    
        <ui:define name="actions_upper">
            <util:edit_actions id="edit_actions_upper" manager="#{manager}"/>
        </ui:define>
    
        <ui:define name="actions_lower">
            <util:edit_actions id="edit_actions_lower" manager="#{manager}"/>
        </ui:define>
    
    </ui:composition>
    

    似乎默默无视。

1 个答案:

答案 0 :(得分:0)

我已经找到了解决方案(真正解决方法)来满足我的要求。不漂亮,但可能对其他人有用。

我没有测试特定的组件ID,而是使用regexp:

/**
 * Searches for a component id ending in ":save".
 * 
 * @return 
 */
public boolean isSaveButtonPressed() {        
    Map<String, String> parameterMap = FacesContext.getCurrentInstance()
            .getExternalContext().getRequestParameterMap();
    for (String key : parameterMap.keySet()) {
        boolean matches = Pattern.matches("^j.*:save",key);
        if (matches) return true;
    }
    return false;
}

所需的测试只是:

<p:inputText 
  id="newLinkUrl"
  value="#{cc.attrs.manager.newLinkUrl}"
  required="#{not cc.attrs.manager.saveButtonPressed}"
  validator="urlValidator"
/>

然后在插入我的util:edit_actions组件时(为了方便用户,它将通过模板显示在表单的顶部和底部)我不会将它传递给显式ID,我只是让JSF生成它们,两个保存按钮都不同(不再发生冲突)。

所以我可以将我的两次注射edit_actions蛋糕吃掉。对于两个“保存”按钮,URL字符串字段上的required测试无法正确执行。