I know what causes the following problem. I am looking for an elegant way to address the problem rather than a brute-force (and non-DRY) workaround I've found.
I have a highly reusable h:form
in a template (and I am aware of the dangers of "god forms", this is not quite a god form), and I insert editable content into that form via the template, with an identical action bar of command buttons both at the top and the bottom of the page. (There hundreds of clients to this template, and some insert different actions bars into the template.)
The only reason I am doing this (bar top and bottom) is user convenience; I have found when using many content management systems that it is annoying to have to scroll down or scroll up to find a Save button (or other buttons in an action bar) on long forms.
The template (please don't tell me it's a "god form") has:
<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>
Every edit.xhtml
page (there are lots of them) that is a client to a template inserts the action bar, along with a compatible #{manager}
backing bean parameter:
<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
Note how above I have given that CC `util:edit_actions' an id (which I did not do on this CC until recently for reasons I'll explain below).
So you can see that the exact same actions toolbar is inserted into the top and bottom just inside form section of a page. If, however, you do this as shown above with an id passed for edit_actions
you get:
javax.servlet.ServletException: Component ID edit_actions has already been found in the view.
I have been using this template with success for years until I introduced the explicit id, for reasons now illustrated below.
The edit_actions
CC has some command buttons such as:
<composite:implementation>
<p:toolbar>
<p:toolbarGroup>
<p:commandButton
ajax ="true"
action="#{cc.attrs.manager.crud.update}"
value="Save"
update="@form"
id="save"
/>
...
Now that general Save button is not always the only button in the form; there are sometimes other buttons that perform interim AJAX actions with conditionally required input fields, such as this from an embedded links table editor:
<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 ..."
/>
(Where BTW that urlValidator
does NOT throw on null, the system relies on the conditional required
for that so that the general @form
Save always works.)
But to get the conditional required to work:
required="#{param['edit_actions:save']==null}"
I have to give the inserted edit_actions
CC an explicit id whenever performing the insert in any of the hundreds of edit.xhtml client pages that use it:
<ui:composition template="/template.xhtml">
...
<ui:define name="actions">
<util:edit_actions id="edit_actions" manager="#{manager}"/>
</ui:define>
But as shown above, if I do include the id there, it now causes an error (but without it I can't use the conditional required
trick).
There are two workarounds I've found so far:
Simply don't have the action bar in the template twice. This is unacceptable, it simply breaks the feature by avoiding it.
Having 2 different insertion points in the template does work, but you have to be careful with the IDs.
And are problems with the 2nd one:
<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>
Note that this code above is not Don't Repeat Yourself (DRY) code, which I consider one of the most important coding practices, and something JSF is usually particularly good at addressing. Ensuring that the above template insertion pattern and id pattern is addressed in hundreds of edit.xhtml
page is just plain error prone, unless I can somehow encapsulate that ui:define
pair while still being able to inject any compatible #{manager}
.
And the conditional required test then has to test on both upper and lower Save buttons:
<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"
/>
All in all, a rather ugly non-DRY workaround.
Q1: Is there any way I can somehow dynamically change the id of an inserted edit_action.xhtml automatically so that it can appear in the template in 2 different places without a clashing component id error ?
Q2: Alternatively, is there some way I can encapsulate the two ui:define
in the workaround for the upper vs lower bar insertion (as show in workaround 2.), while still having the ability to inject the #{manager}
(so that I can include it and reuse it as encapsulated policy in hundreds of edit.xhtml clients to the template) ?
EDIT: this attempt to encapsulate my "double action bar" pattern does not seem to work. From /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>
With attempted use by edit.xhtml
:
<ui:composition template="/template.xhtml">
<ui:include src="/include/edit_actions_defines.xhtml">
<ui:param name="manager" value="#{specificManager}"/>
</ui:include>
Seems to be ignored silently.