JSF 2.0: Passing composite component attribute to inner composite component - jsf

I have the following case:
<cc:interface>
<cc:attribute name="someValue" />
</cc:interface>
<cc:composite>
<x:someComponent>
<span>#{cc.attrs.someValue}</span>
</x:someComponent>
</cc:composite>
So inside my composite component I am calling some other composite component and trying to pass the parameter given to the "master" composite component to the inner composite component.
This fails, because inside x:someComponent tags the cc implicit object seems to refer to this x:someComponent instead.
A workaround is to create a temporary field for the x:someComponent so this can be achieved as:
<x:someComponent passthroughField="#{cc.attrs.someValue}">
<span>#{cc.attrs.passthroughField}</span>
</x:someComponent>
However that's very ugly and unconvenient.
Any other ways around this problem?

One way to hack around this is to use ui:param as in:
<ui:param name="foo" value="cc.attrs.someValue" />
<x:someComponent>
<span>#{foo}</span>
</x:someComponent>
See more in another question.

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.

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

Reusing a form in two different views JSF

Is it possible to reuse a form in two different views in JSF? I know about the ui:include but how would you do it with the action on the submit button for example? Or do I need to duplicate the forms?
When using <ui:include> you can parameterize the bean and/or the method by <ui:param>.
<ui:include src="...">
<ui:param name="bean" value="#{someBeanName}" />
<ui:param name="action" value="someMethodName" />
</ui:include>
with
<h:commandButton ... action="#{someBeanName[someMethodName]}" />
You can also make it a Facelets tag file instead so that you end up with something like
<my:someForm bean="#{someBeanName}" action="someMethodName" />
A composite component is also doable, but IMO not really the right approach for this purpose.
See also:
When to use <ui:include>, tag files, composite components and/or custom components?
Our Composite Component wiki page
You want to use composite components. Those components can be parameterized so that you could exchange the backing bean, etc..
IBM has some nice guides on that topic.
Composite components part 1 + 2 and best practices.

Decorating standard components in composite components, how to inherit attributes?

I am wondering if there is a nice way to decorate components with composite components?
Example:
<composite:implementation>
<div style="someFancyClass">
<h:inputText value="#{cc.attrs.value}" />
</div>
</composite:implementation>
In this case the value attribute is passed through to the contained <h:inputText>. But what about all the other attributes? Do I have to declare all of them in the <composite:interface> section?
It would be nice to have some kind of inheritance from standard components, so that e.g. the maxlength attribute of <h:inputText> is automatically available at the composite component.
Do I have to declare all of them in the <composite:interface> section?
Not necessary, you can just use maxlength="#{cc.attrs.maxlength}" without the need to declare it as <composite:attribute>. However, this is bad for documentatory purposes. The developer would not see this attribute to appear in the composite component's documentation (which might be used by IDE autocompletion, for example).
It would be nice to have some kind of inheritance from standard components, so that e.g. the maxlength attribute of <h:inputText> is automatically available at the composite component.
That's not possible. For that you'd really need to create a fullworthy custom UIInput component and/or a Renderer (in your particular case, just the renderer ought to be sufficient).
I provided an example on how to decorate a composite component here https://stackoverflow.com/a/8881510/1151983
However, this does not provide real inheritance, but a way to share common stuff between a set of similar composite components.

Resources