我知道是什么导致了以下问题。我正在寻找一种优雅的方法来解决问题,而不是我发现的蛮力(和非 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
,以便一般@form
Save 始终有效。)
但是要获得工作所需的条件:
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
技巧)。
到目前为止,我发现了两种解决方法:
只是没有两次模板中的操作栏。这是不可接受的,它只是通过避免它来破坏该功能。
在模板中有 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>
似乎被默默地忽略了。