2

我知道是什么导致了以下问题。我正在寻找一种优雅的方法来解决问题,而不是我发现的蛮力(和非 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,原因如下所示。

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

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

现在,一般的保存按钮并不总是表单中的唯一按钮;有时还有其他按钮可以使用有条件的输入字段执行临时 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 ..."
  />

(顺便说一句,urlValidator在不抛出 null 的情况下,系统依赖于条件required,以便一般@formSave 始终有效。)

但是要获得工作所需的条件:

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

每当在使用它的数百个edit.xhtml 客户端页面中的任何一个中执行插入时,我都必须给插入的edit_actionsCC 一个显式 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。

第二个问题是:

<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对,同时仍然能够注入任何兼容#{manager}的 .

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

<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"
 />

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

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

Q2:或者,是否有某种方法可以将两者封装在ui:define上杆和下杆插入的解决方法中(如解决方法 2 中所示),同时仍然能够注入#{manager}(以便我可以包含它并重用它作为模板中数百个edit.xhtml客户端的封装策略)?


编辑:这种封装我的“双动作栏”模式的尝试似乎不起作用。来自/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>

尝试使用edit.xhtml

<ui:composition template="/template.xhtml">

    <ui:include src="/include/edit_actions_defines.xhtml">
        <ui:param name="manager" value="#{specificManager}"/>            
    </ui:include>

似乎被默默地忽略了。

4

1 回答 1

0

我已经找到了满足我要求的自己的问题的解决方案(实际上是解决方法)。不漂亮,但可能对其他人有用。

我没有测试特定的组件 ID,而是使用正则表达式:

/**
 * 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蛋糕。required对两个保存按钮的 URL 字符串字段的测试正确失败。

于 2017-04-25T13:51:48.413 回答