How to create a composite component which switches between inputText and inputSecret? - jsf

I'm writing a Facelets composite component that switches between using inputText and inputSecret based on a parameter:
<composite:interface>
<composite:attribute name="myId" required="true"/>
<composite:attribute name="secret" required="false" default="false" />
</composite:interface>
<composite:implementation>
<h:inputSecret rendered="#{cc.attrs.secret}" id="#{cc.attrs.myId}" />
<h:inputText rendered="#{!cc.attrs.secret}" id="#{cc.attrs.myId}" />
</composite:implementation>
The problem is that I get the following error:
Component ID [JSF mangled id] has already been found in the view.

Use a view build time tag like JSTL <c:if> or <c:choose> instead of the JSF component's rendered attribute. View build time tags are evaluated during constructing the JSF component tree, while the rendered attribute is only evaluated during generating HTML based on the JSF component tree (and thus you still end up with both components with the same ID in the JSF component tree!).
E.g.
<c:if test="#{not cc.attrs.secret}">
<h:inputText id="input" />
</c:if>
<c:if test="#{cc.attrs.secret}">
<h:inputSecret id="input" />
</c:if>
See also:
JSTL in JSF2 Facelets... makes sense?
Unrelated to the concrete problem, the myId doesn't make sense. Just give those a fixed ID. In case the reason was the inability to reference them from outside by ajax, head to Referring composite component ID in f:ajax render.

Whether or not the component is actually rendered doesn't matter.Both components will still exist in the view's internal component tree and will require a unique id. We ran into this problem as well.
We suffixed the id with a _1 and _2 and if we need to get a hold of the id inside javaScript, we use JQuery's partial matchers.
In your case, can you not make your bean's getMyId() method return a different id based on the value of the secret property?

Related

How to reference component inside a composite component when using a converter

I have a composite component that mainly consists of a selectManyCheckbox component. As it should be designed in a generic way I pass in selectItems, ajax handling etc. from the calling level using
<composite:insertChildren/>
This works quite well for most of the stuff. Now I need to use this composite component with a converter. As the converter (a kind of Omnifaces' ListConverter) is not needed all the time (sometimes I want to have the value-binding of concrete entities that back the select items, sometimes I don't), I'd like to pass it in as the parts mentioned before (e.g. selectItems, ajax event handling).
Given this it is necessary to use the converter tag's 'for' attribute to reference the component inside the composite component. At least that is what I understand.
Unfortunately I have no idea what value should be used. Do I have to include the name of the composite component (naming container)? Do I have to use the clientId? I have tried a lot of combinations but the converter has not been invoked. As soon as I put the converter tag inside the composite component definition, it works.
To make things easier, let's assume I have the following:
<composite:interface>
<composite:attribute name="value" required="true"/>
</composite:interface>
<composite:implementation>
[...]
<h:selectManyCheckbox id="#{cc.attrs.id}" value="#{cc.attrs.value}">
<composite:insertChildren/>
</h:selectManyCheckbox>
[...]
</composite:implementation>
This component should be used as follows:
<my:selectManyCheckbox id="myComponent" value="...">
<f:selectItems value="..."/>
<o:converter for="___" converterId="..."/>
</my:selectManyCheckbox>
Perhaps someone can give me a hint what value should be given to the 'for' attribute.
I found the answer myself. You can use the following
<composite:interface>
<composite:attribute name="value" required="true"/>
<composite:editableValueHolder name="input_component">
</composite:interface>
<composite:implementation>
[...]
<h:selectManyCheckbox id="input_component" value="#{cc.attrs.value}">
<composite:insertChildren/>
</h:selectManyCheckbox>
[...]
</composite:implementation>
The important part is the editableValueHolder tag. From the page using this composite component, you can now use
<o:converter for="input_component" converterId="id_of_converter" />
This is the solution that is working for me. I hope this helps others having the same problem.

Send parameter to request on every action for composite component

I have a composite component, which has an id I would like to send as a parameter when executing one of many posiible actions inside the composite component. I know I can use something like;
<h:form id="testForm">
<p:commandButton value="#{testReqBean.label}"
actionListener="#{testReqBean.perform()}"
process="#this or #form" update="#form" ajax="true" >
<f:param value="#{cc.attrs.id}" name="CC-Id" />
</p:commandButton>
</h:form>
now, imagine I have many forms or buttons with specific actions inside the composite component... is there a way to define the parameter I want to send in the request just once ? I mean not adding an f:param inside each form/button (depending on the process #form or #this) but one for the whole composite component?
Thanks in advance!
Maybe one solution would be to use viewparam but this only works if you can add a request parameter.
<f:metadata>
<f:viewParam value="#{your_bean.your_property_name}" name="request_param"/>
</f:metadata>
The only problem here is that whoever implements your composite component would have to set the above when needed, but it still an abstraction to this problem of having to set the same property for all components in same page.
I gather that the <h:form> is enclosed in the composite component itself.
Just use a plain HTML hidden input field.
<h:form>
<input type="hidden" name="CC-Id" value="#{cc.attrs.id}" />
...
<p:commandButton />
<p:commandButton />
<p:commandButton />
...
</h:form>
Unrelated to the concrete problem, having an entire form in a composite is kind of strange. This is then food for read: When to use <ui:include>, tag files, composite components and/or custom components?

Accessing JSF nested composite component elements in JavaScript

I am trying to DRY up popup windows in my JSF 2 project using composite components.
This code base uses Icefaces 3.3.0 (with their 1.8.2 compatibility layer for historical reasons), Mojarra 2.2.7, and Glassfish 4.1.
I have input.xhtml which provides a text input and uses a 2-button popup (ok/cancel), which in turn builds on the basic popup.
input.xhtml:
<composite:interface>
<!-- ... -->
<composite:editableValueHolder name="forInput" targets="theInput"/>
</composite:interface>
<composite:implementation>
<my:popup2Buttons>
<ice:inputText id="theInput" value="..."/>
<script>setInputFocus("#{cc.clientId}:theInput");</script>
</my:popup2Buttons>
</composite:implementation>
popup2buttons.xhtml:
<composite:interface>
<!-- ... -->
</composite:interface>
<composite:implementation>
<my:popup>
<composite:insertChildren/>
<ice:commandButton id="OkButton"
value="Ok"
actionListener="..."/>
<ice:commandButton id="CancelButton"
value="Cancel"
actionListener="..."/>
</my:popup>
</composite:implementation>
popup.xhtml:
<composite:interface>
<!-- ... -->
</composite:interface>
<composite:implementation>
<script>
function setInputFocus(id) {
document.getElementById(id).focus();
}
</script>
<ice:panelPopup>
<f:facet name="body">
<h:panelGroup>
<composite:insertChildren/>
</h:panelGroup>
</f:facet>
</ice:panelPopup>
</composite:implementation>
The popup works mostly as expected, i.e., I can enter something, the ok and cancel buttons work, and validation works as well.
What does not work is my JavaScript code that tries to focus the input when the popup opens.
When I look at the page in Firebug, I see that the input's ID is MyForm:j_idt63:j_idt64:j_idt67:theInput, but the JavaScript code tries to focus an element with the ID MyForm:j_idt63:theInput.
Why is #{cc.clientId} in input.xhtml not the correct ID that the input ends up getting later? What do I need to do to make this work?
I've seen BalusC's hint on adding a binding but I don't want a binding so that the composite component can be independent of any backing beans.
Is there something else I am missing here?
Composite components are implicitly naming containers. I.e. they prepend their ID to the client ID of the children. This makes it possible to use multiple of them in the same view without their children causing duplicate IDs in generated HTML output.
In your specific case, you wrapped the input field in another composite which is in turn wrapped in again another composite. If you're absolutely positive that you don't need multiple naming containers wrapping in each other in this specific composition, then those (popup2buttons.xhtml and popup.xhtml) probably shouldn't be composites, but rather <ui:decorate> templates or <ui:composition> tagfiles. See also When to use <ui:include>, tag files, composite components and/or custom components?
Coming back to the technical problem, it's caused because the #{cc.clientId} does not refer the ID of the nested composite component, but of the current composite component. And thus this would be off. As to the potential solution with binding, the answer which you found does nowhere tell that you should use a backing bean for this. The binding="#{foo}" code in the answer was as-is. It really works that way, without a bean property, see also JSF component binding without bean property. However, this construct would indeed fail when you include the same composite multiple times in the same view and thus multiple components share the same binding="#{foo}". It indeed isn't supposed to be shared by multiple components, see also What is component binding in JSF? When it is preferred to be used?
To solve this without a backing bean, you can use a so-called backing component.
com.example.InputComposite
#FacesComponent("inputComposite")
public class InputComposite extends UINamingContainer {
private UIInput input;
// +getter+setter.
}
input.xhtml
<cc:interface componentType="inputComposite">
...
</cc:interface>
<cc:implementation>
...
<h:inputText binding="#{cc.input}" ... />
<script>setInputFocus("#{cc.input.clientId}");</script>
...
</cc:implementation>
The alternative is to rework them into templates or tagfiles. Be careful that you don't overvalue/overuse composites.

#{cc.clientId} evaluated in wrong composite after upgrading to JSF 2.2

I have a tag library which was written in JSF 2.0 + PrimeFaces 3.4, now i am trying to update to JSF 2.2 and PrimeFaces 4.0.
But i realized that the value of attributes passed to component evaluated in composite component and it leads to wrong id for rendering.
enum.xhtml (composite component)
<cc:interface>
<cc:attribute name="render" default="#this"/>
.....
</cc:interface>
<cc:implementation>
<h:selectOneMenu ......../>
<p:ajax update="#{cc.attrs.render}" process="#{cc.attrs.execute}" />
</cc:implementation>
usage :
<t:enum id="authenticationSource" value="#{authenticationStrategy}" .....
render=":#{cc.clientId}:tabView:passwordVisibility"/>
render attribute value which is :#{cc.clientId}:tabView:passwordVisibility, should be
:j_idt1:j_idt3:j_idt5:editorDialog:j_idt39:j_idt40:tabView:passwordVisibility`
But it is evaluated as
:j_idt1:j_idt3:j_idt5:editorDialog:j_idt39:j_idt40:tabView:autheticationSource:tabView:passwordVisibility
Attribute value of render is evaluated in composite component and it caused to error. It should be evaluated where it is used and it was like that in JSF 2.0.
Is there any configuration property or anything to overcome this error.
I'm using wildfly 8.1.0-Final
This composite is not rightly designed. You're not supposed to use #{cc.clientId} outside the composite's context. More generally, you're not supposed to know anything about the composite's internals from outside the composite. The composite itself should worry about this.
This construct will fail if you're nesting composite components in each other. The #{cc} would then actually refer the "current" composite component. Perhaps you were relying on a bug in an older JSF implementation where the #{cc} scope isn't properly being cleared out after the nested composite component (i.e. it would refer the last assigned value instead of the value available in the current context).
Perhaps you're victim of overusing composite components for the wrong puspose only and only because of the zero-configuration nature as compared to regular tagfiles/includes. For detail as to when exactly to use the one or other, head to When to use <ui:include>, tag files, composite components and/or custom components? To the point, use a composite only and only if you want to bind a bunch of closely related components to a single bean property, and thus certainly not to a "whole" bean with several properties.
If you're absolutely positive that a composite is the right solution for your requirement, and/or you've refactored the composite accordingly to eliminate the mentioned misuse, then there are 2 possible approaches for applying client behavior on the composite component, depending on the concrete functional requirement (you can even combine the both ways if necessary).
If you want to let the composite ajax-render a component outside the composite, externalize <p:ajax> (or <f:ajax>) as <cc:clientBehavior>:
<cc:interface>
<cc:clientBehavior name="myCustomEventName" targets="idOfTargetComponent" event="valueChange" />
...
</cc:interface>
<cc:implementation>
<h:selectOneMenu id="idOfTargetComponent" ...>
<f:selectItems ... />
</h:selectOneMenu>
</cc:implementation>
Which is to be used as:
<t:enum ...>
<p:ajax event="myCustomEventName" update=":absoluteClientIdOfComponentOUTSIDEComposite" />
</t:enum>
<x:someComponent id="idOfComponentOUTSIDEComposite" />
If you want to let the composite ajax-render a component inside the composite, then let the composite do it all by itself.
<cc:interface>
...
</cc:interface>
<cc:implementation>
<h:selectOneMenu ...>
<f:selectItems ... />
<p:ajax update="idOfComponentINSIDEComposite" />
</h:selectOneMenu>
<x:someComponent id="idOfComponentINSIDEComposite" />
</cc:implementation>
And use it the usual way:
<t:enum ... />

How to stop recursive composite component from including itself recursively

Is it possible to have a JSF component that has ui:repeat and inside the repeat call the same component? It's because I'm building tree of question:
<cc:interface>
<cc:attribute name="questions" required="true" />
<cc:attribute name="renderQuestions" required="true" default="true" />
</cc:interface>
<cc:implementation>
<c:if test="#{cc.attrs.renderQuestions}">
<ui:repeat value="#{cc.attrs.questions}" var="q">
<p:panelGrid columns="2">
<h:outputLabel value="#{q.question}"></h:outputLabel>
<p:selectBooleanButton onLabel="#{messages['commons.yes']}"
offLabel="#{messages['commons.no']}" onIcon="ui-icon-check"
offIcon="ui-icon-close" value="#{q.ok}">
<p:ajax update="#all"></p:ajax>
</p:selectBooleanButton>
</p:panelGrid>
<cf:question renderQuestions="#{q.ok}" questions="#{q.children}" />
</ui:repeat>
</c:if>
</cc:implementation>
Currently I'm having stackoverflow?
Use a view build time tag to stop the recursion instead of a view render time tag. The component tree is built during view build time. The rendered attribute isn't evaluated during view build time, but instead during view render time. So your construct basically keeps including itself in an infinite loop. You should already know that StackOverflowError is caused by a recursion so deep that the memory stack can't handle it anymore (usually around ~1000 iterations).
Replace <h:panelGroup rendered> by a <c:if test> and it'll work as expected. The renderQuestions can by the way be simplified by omitting the check on children. The <ui:repeat> won't render anything anyway if there are no children.
If you happen to use view scoped beans in this construct and you're using Mojarra implementation, then make sure that you upgrade to at least 2.1.18, because binding a view build time tag attribute to a view scoped bean property would in older versions break the view scope.
See also:
JSTL in JSF2 Facelets... makes sense? - explains "view build time" versus "view render time"
Based on the answer of BalusC I created a solution like this.
<composite:interface>
[..]
<composite:attribute name="level" required="true" default="0"/>
</composite:interface>
<composite:implementation>
<c:if test="#{cc.attrs.level == 0}">
<a4j:repeat value="#{cc.attrs.list}" var="subList" rendered="#{cc.attrs.list != null}">
<xx:recursiveComponent list="#{subList}" id="subComponent" level="1"/>
</a4j:repeat>
</c:if>
</composite:implementation>
The level attribute is used at build time to avoid endless recursion and the renered attribute of the a4j:repeat (or ui:repeat in your case) will be evaluated at render time.

Resources