Passing markup via composite components attributes - attributes

Is there a way to pass markup (in opposite to plain text) via composite
component's attributes? Simply <composite:insertChildren> won't suffice,
since the component rely on distinct text parameters. Passing tags via
attributes as described in Include sub-element inside JSF 2.0 component does not work (invalid attribute content).

You need to declare it as <cc:facet> and render it as <cc:renderFacet>:
<cc:interface>
<cc:facet name="foo" />
</cc:interface>
<cc:implementation>
<cc:renderFacet name="foo" />
</cc:implementation>
This way you can specify it using <f:facet>:
<my:composite>
<f:facet name="foo">
<p>Some <strong>HTML</strong> markup.</p>
</f:facet>
</my:composite>

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.

setPropertyActionListeners to composite component

I need to create composite component containing two h:commandLinks. And i want to pass f:setPropertyActionListeners from the client-code to be applied to both two commandLinks. Is this ever possible? I tried to use cc:insertChildren, but appropriate setters are not being fired.
<my:operationLink action="#{cc.attrs.bean.myAction}">
<f:setPropertyActionListener for="<!-- whats here? -->" value="#{cc.attrs.someAttrOne}" target="#{cc.attrs.bean.someAttrTargetOne}"/>
<f:setPropertyActionListener for="<!-- whats here? -->" value="#{cc.attrs.someAttrTwo}" target="#{cc.attrs.bean.someAttrTargetTwo}"/>
and my component:
<cc:implementation>
<h:commandLink id="textLink" value="myTextLink"><ui:insert/></h:commandLink>
<h:commandLink id="imgLink"><h:graphicImage url="/images/my.gif"/><ui:insert/></h:commandLink>
i need to apply actionlisteners to both links ( into ui:insert)
You need to declare a <cc:actionSource> in the composite interface with the "event name" in name (e.g. actionEvent, this is fully arbitrary to your choice) and the client IDs of those command links space separated in the targets.
<cc:interface>
<cc:actionSource name="actionEvent" targets="textLink imgLink" />
</cc:interface>
Then you can use in the client:
<f:setPropertyActionListener for="actionEvent" ... />
Don't forget to remove <ui:insert>. This is indeed definitely not the right way.

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

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?

Parameterization of facelet custom tag

I have created facelet template:
left-right.xhtml
<ui:composition>
<ui:include name="left" />
<hr />
<ui:include name="right" />
</ui:composition>
After, if I use this template with ui:decorate it works fine:
index.xhtml
<ui:decorate template="left-right.xhtml">
<ui:define name="left">FOO</ui:define>
<ui:define name="right">BAR</ui:define>
</ui:decorate>
BUT, if I use this template as custom facelet tag it does not works.
custom-taglib.xml
<facelet-taglib>
<tag>
<tag-name>leftright</tag-name>
<source>left-right.xhtml</source>
</tag>
</facelet-taglib>
index.xhtml
<custom:leftright>
<ui:define name="left">FOO</ui:define>
<ui:define name="right">BAR</ui:define>
</custom:leftright>
The content inside ui:define tags is not included into template :(
So, question is how can I parameterise facelet template if it renders as facelet custom tag?
(note that you have a syntax error in your left-right.xhtml, you should be using <ui:insert> instead of <ui:include>, but I'll assume it to be just careless oversimplification)
A tag file cannot be treated as a template client. You need to approach it differently depending on the concrete functional requirement. If you're on JSF 2.x, then a composite component would be the closest which you need. You could define the parts as <f:facet> and render them by <cc:renderFacet> in the composite implementation.
E.g.
/resources/custom/leftRight.xhtml
<cc:interface>
<cc:facet name="left" required="true" />
<cc:facet name="right" required="true" />
</cc:interface>
<cc:implementation>
<cc:renderFacet name="left" />
<hr />
<cc:renderFacet name="right" />
</cc:implementation>
Usage:
<custom:leftRight>
<f:facet name="left">FOO</f:facet>
<f:facet name="right">FOO</f:facet>
</custom:leftRight>
But if you're still on JSF 1.x, you cannot create a composite component. You'd need to stick to <ui:decorate>.
See also:
When to use <ui:include>, tag files, composite components and/or custom components?

Custom data types in Facelets JSF 2 Expression Language

How to display a custom property using facelet expression language?
For example:
<h:outputText value="#{contact.customTypeProperty}" />
where customTypeProperty is of type CustomClass, and I want to display the String returned by its toString()?
That should already be the default behaviour. You don't need to change anything on the given code example, assuming that the toString() method is properly implemented on the CustomClass. However, if it returns HTML, you'd need to add escape="false" to the output text to prevent JSF from auto-escaping it (which it does in order to prevent XSS attacks on user-controlled input):
<h:outputText value="#{contact.customTypeProperty}" escape="false" />
This is however not necessarily the best practice. You should control the presentation in the view side, not in a toString() in the model side. For example, assuming that CustomClass has in turn two properties foo and bar and you'd like to present it in a table:
<h:panelGrid columns="2">
<h:outputText value="Foo" />
<h:outputText value="#{contact.customTypeProperty.foo}" />
<h:outputText value="Bar" />
<h:outputText value="#{contact.customTypeProperty.bar}" />
</h:panelGrid>
If you did this to avoid code repetition, then you should actually be using an include file or a tag file. See also When to use <ui:include>, tag files, composite components and/or custom components?

Resources