I have a composite component with a ui:repeat and want to define the content of the ui:repeat through the interface of the composite.
Following code is working in MyFaces but looks more like a hack since variable name varRepeat must be known outside of composite and it only works if no other childrens are provided that should be rendered somewhere else.
View
Define content of the ui:repeat
<comp:myRepeater value="#{of:createIntegerArray(1,5)}">
<h:outputText id="varComp" value="#{varRepeat}"/>
</comp:myRepeater>
Composite myRepeater
<composite:attribute name="value" type="java.lang.Object"/>
<composite:implementation>
<ui:repeat var="varRepeat" value="#{cc.attrs.value}">
<composite:insertChildren/>
</ui:repeat>
</composite:implementation>
That's the best you can get given that var attribute doesn't support EL. To make it clear to the enduser, document the name of var in <cc:interface shortDescription> and/or <cc:attribute shortDescription>.
<cc:interface>
<cc:attribute name="value" type="java.util.Collection"
shortDescription="A collection of items. Each item is exposed in EL under the variable name 'item'." />
<cc:interface>
<cc:implementation>
<ui:repeat value="#{cc.attrs.value}" var="item">
<cc:insertChildren />
</ui:repeat>
</cc:implementation>
Usage:
<my:repeat value="#{bean.list}">
<h:outputText value="#{item}" />
</my:repeat>
The OmniFaces showcase application has also a similar composite for long: <os:listDocs>:
<cc:implementation>
<c:set var="docs" value="#{page.current.documentation[cc.attrs.paths]}" />
<ui:fragment rendered="#{not empty docs}">
<h3>#{cc.attrs.header}</h3>
<ul>
<ui:repeat value="#{docs}" var="path">
<li><code>#{cc.attrs.label}</code></li>
</ui:repeat>
</ul>
</ui:fragment>
</cc:implementation>
Usage:
<os:listdocs header="VDL" paths="vdl" url="#{_vdlURL}#{path}.html" label="#{fn:replace(path,'/', ':')}" />
<os:listdocs header="API" paths="api" url="#{_apiURL}#{path}.html" label="#{fn:replace(path,'/', '.')}" />
<os:listdocs header="Source code" paths="api" url="#{_srcURL}#{path}.java" label="#{fn:replace(path,'/', '.')}" />
As a design hint, if you use a sensible attribute name for the collection, the var may become more self-documenting. E.g. ask for items as value and provide a var="item".
<my:repeat items="#{[1,2,3,4,5]}">
<h:outputText value="#{item}"/>
</my:repeat>
Related
I am using JSF Mojarra 2.3.17 with the following page content:
<dcoco:modul id="login" name="Login">
<h:form id="form" prependId="true">
<h:outputLabel id="idLoginUsernameLabel" value="Name:" />
<h:inputText id="idLoginUsername" value="#{loginForm.username}">
<f:ajax event="change" execute="#this" render="idUserOutput" />
</h:inputText>
<h:panelGroup id="idUserOutput">#{loginForm.username}</h:panelGroup>
</h:form>
</dcoco:modul>
With the following definition of the composite component:
<cc:interface>
<cc:attribute name="name" required="true"/>
</cc:interface>
<cc:implementation>
<h:panelGroup>
<h1>#{cc.attrs.name}</h1>
<cc:insertChildren />
</h:panelGroup>
</cc:implementation>
When leaving the input field, idUserOutput is not updated as I would have expected. I think this is because the corresponding JS renders as follows (I am using "_" as separator char):
onchange="mojarra.ab(this,event,'change','login','login_form_idUserOutput')"
Which uses the ID of the composite component and not the ID of the input field in the fourth parameter.
If I change any of the following, it works:
execute="idLoginUsername"
remove the composite component
revert to Mojarra 2.3.14
Did the rendering behaviour of #this change with the new Mojarra version? What do I have to change to refer to the input field in f:ajax (I cannot use the ID of the input field in my real-world application)?
Basic Question
Is this a bug in Mojarra or am I trying to do something that is not supported by the spec?
Overview
I have a composite component "outerComposite" that ui:includes a facelet with another composite component "innerComposite". I want to pass the client id of the outer composite to the inner composite using <ui:param>. Depending on how the EL expression looks like, this works or fails on Wildfly 10/11 but works in all cases on Websphere Liberty 17.0.0.1.
Basic Code
outerCompositeUsingPage.xhtml
<h:head />
<h:body>
<h:form id="form">
<app:outerComposite id="outer" value="Test" />
</h:form>
</h:body>
outerComposite.xhtml that includes a facelet with innerComposite:
<cc:implementation>
<c:set var="clientIdViaSet" value="#{cc.clientId}" />
<div id="#{cc.clientId}">
<ui:include src="innerCompositeInclude.xhtml">
<ui:param name="clientIdDirect" value="#{cc.clientId}" /> <-- WORKS
<ui:param name="clientIdViaMethod" value="#{compositeHelperBean.getClientId(cc)}" /> <-- FAILS
<ui:param name="clientIdViaSet" value="#{clientIdViaSet}" /> <-- WORKS
</ui:include>
</div>
innerCompositeInclude.xhtml
<ui:composition xmlns="http://www.w3.org/1999/xhtml"
xmlns:ui="http://java.sun.com/jsf/facelets"
xmlns:app="http://xmlns.jcp.org/jsf/composite/components/app" >
<app:innerComposite id="inner"
value1Key="Direct"
value1Value="#{clientIdDirect}"
value2Key="Via Method"
value2Value="#{clientIdViaMethod}"
value3Key="Via c:set"
value3Value="#{clientIdViaSet}"
/>
</ui:composition>
innerComposite.xhtml
<cc:interface>
<cc:attribute name="value1Key" type="java.lang.String" />
<cc:attribute name="value2Key" type="java.lang.String" />
<cc:attribute name="value3Key" type="java.lang.String" />
<cc:attribute name="value1Value" type="java.lang.String" />
<cc:attribute name="value2Value" type="java.lang.String" />
<cc:attribute name="value3Value" type="java.lang.String" />
</cc:interface>
<cc:implementation>
<div id="#{cc.clientId}">
<h:panelGrid columns="2">
<h:outputText value="#{cc.attrs.value1Key}" />
<h:outputText value="#{cc.attrs.value1Value}" />
<h:outputText value="#{cc.attrs.value2Key}" />
<h:outputText value="#{cc.attrs.value2Value}" />
<h:outputText value="#{cc.attrs.value3Key}" />
<h:outputText value="#{cc.attrs.value3Value}" />
</h:panelGrid>
</div>
</cc:implementation>
ComponentHelperBean.java
#ApplicationScoped
#Named
public class CompositeHelperBean {
public String getClientId(UIComponent comp) {
return comp.getClientId();
}
}
Observation
Because #{cc.clientId} is passed as a parameter to an include file, I expect #{cc.clientId} to be the value of the outer composite. This indeed works.
Because #{compositeHelperBean(cc)} just calls cc.getClientId(), I expect this to yield the same result as the direct EL expression #{cc.clientId}. This is not the case; instead, it resolves to the client id of the inner composite component.
The result looks as follows in Wildfly 10/11:
Direct form:outer <--- OK
Via Method form:outer:inner <--- WRONG
Via c:set form:outer <--- OK
The result looks as follows in Websphere Liberty Profile 17.0.0.1
Direct form:outer <--- OK
Via Method form:outer <--- OK
Via c:set form:outer <--- OK
Research
I have looked into the code of Mojarra as well as MyFaces. Both have a TagHandlerImpl that create a ValueExpression. In there, they use a regular expression to determine if the EL has something to do with cc. Both expressions are different which could explain why the behaviour is different.
I'm having one or two problems with Primefaces (v5.2):
Referencing components inside composite components
Let's say I have a composite component that wraps an inputfield:
myinputfield.xhtml
<composite:interface>
<composite:attribute name="value" />
...
</composite:interface>
<composite:implementation>
<h:inputText value="#{cc.attrs.value}" />
</composite:implementation>
(Of course the real application does "a little" more.)
In my page I now use this field like this:
index.xhtml
<my:myinputputfield value=#{controller.inputstring} />
This works. But:
Know I want to reference that inner inputfield from outside, for example for labels or messages. Something like:
<p:inputLabel for="mif" value="Your Input:"/>
<my:myinputputfield id="mif" value=#{controller.inputstring} />
<p:message for="mif" />
Of course that doesn't work, because id isn't defined for myinputfield.
So the first idea that pops to mind is to extent the cc like this:
myinputfield.xhtml (new)
<composite:interface>
<composite:attribute name="id" />
<composite:attribute name="value" />
...
</composite:interface>
<composite:implementation>
<h:inputText id="{cc.attrs.id}" value="#{cc.attrs.value}" />
</composite:implementation>
Which does not work as well. I tried different things and read different answers and articles without finding an answer to this.
The second problem is the complete opposite:
Referencing components outside composite components
This time imagine it the other way around. I have a customized label, message or in my case a tooltip:
mytooltip.xhtml
<composite:interface>
<composite:attribute name="for" />
<composite:attribute name="value" />
...
</composite:interface>
<composite:implementation>
<p:toolTip for="#{cc.attrs.for}" value="#{cc.attrs.value}" />
</composite:implementation>
This time I want to attach mytooltip to an existing component:
index.xhtml
<h:outputtext id="ot" value="Hello World!" />
<my:mytooltip for="ot" value="since 1974" />
Which also does not work. (Of course!?)
This problem I had some time ago and solved it by inclduing the outputText in the composite component.
But I have the feeling it should be possible to manage both user cases. But how?
Referencing components inside composite components
give the internal input a static id
<composite:interface>
<composite:attribute name="value" />
...
</composite:interface>
<composite:implementation>
<h:inputText id="input" value="#{cc.attrs.value}" />
</composite:implementation>
reference the internal component as with any naming container:
<p:inputLabel for="mif:input" value="Your Input:"/>
<my:myinputputfield id="mif" value=#{controller.inputstring} />
<p:message for="mif:input" />
Referencing components outside composite components
the canonical way is to use the full client id:
<h:form id="form">
<h:outputText id="ot" value="Hello World!" />
<my:mytooltip for=":form:ot" value="since 1974" />
</h:form>
but, since you are passing the search expression to a PF component, you can also:
<h:form>
<h:outputText id="ot" value="Hello World!" />
<my:mytooltip for="#form:ot" value="since 1974" />
</h:form>
or generically:
<p:tabView>
<p:tab title="random tab">
<h:outputText id="ot" value="Hello World!" />
<my:mytooltip for="#namingcontainer:ot" value="since 1974" />
</p:tab>
</p:tabView>
or even:
<h:outputText value="Hello World!" />
<my:mytooltip for="#composite:#previous" value="since 1974" />
however, in such cases, a tag-component/facelet-tag-file could be a better approach.
Well.
I created a custom component to CRUD and the form page is a parameter.
In this case I need two pages, one to define the component and another with the form.
Is there any way to do that?
As follow:
Implementation:
Component
<cc:interface>
<cc:attribute name="formPage" type="java.lang.String" required="true"/>
.....
</cc:interface>
<cc:implementation>
...
<h:form id="form-crud">
<ui:include src="#{cc.attrs.formPage}" />
</h:form>
...
</cc:implementation>
Page 1: crud.xhtml
<ui:define name="content-template" >
<comp:crud
paginaForm="crud-form.xhtml" />
</ui:define>
Page 2: crud-form.xhtml
<html ...
<p:panelGrid id="grid-crud" columns="2" >
<h:outputLabel for="dsName" value="Name: " />
<p:inputText id="dsName" value="#{crudBean.dsName}"/>
</p:panelGrid>
</html>
New Implementation (sample)
To simplify, I would like to have component defnition and form in the same page, something like that. Is it possible?
I know I could use a template, but the custom componet has more attributes.
New Component
<cc:implementation>
...
<h:form id="form-crud">
<XX:SOMETING name="#{cc.attrs.formContent}" />
</h:form>
...
</cc:implementation>
New Page 1: new-crud.xhtml
...
<ui:define name="content-template" >
<comp:crud
form="new-form" />
<XX:SOMETING id="new-form">
<p:panelGrid id="grid-crud" columns="2" >
<h:outputLabel for="dsName" value="Name: " />
<p:inputText id="dsName" value="#{crudBean.dsName}"/>
</p:panelGrid>
</XX:SOMETING>
</ui:define>
...
You can make use of <f:facet> exactly like as those work with e.g. <h:dataTable>/<h:column> header/footer.
First declare a <cc:facet> with the desired name:
<cc:interface>
<cc:facet name="form" />
</cc:interface>
Then declare a <cc:renderFacet> in the desired place where it should end up:
<cc:implementation>
...
<h:form>
<cc:renderFacet name="form" />
</h:form>
...
</cc:implementation>
Now you can use it as follows:
<comp:crud>
<f:facet name="form">
...
</f:facet>
</comp:crud>
Please note that this construct is also possible with "plain vanilla" Facelets tagfiles via <ui:define>/<ui:insert> mechanisms.
hi i want to pass a bean as parameter composite component,
then i will call its function as an action.
<cc:attribute required="true" name="daobean" />
<ui:param name="daobeann" value="#{cc.attrs.daobean}" />
<p:commandButton action="#{daobeann.update}" />
and i use the component as
<util:mycomponent daobean="mybean" />
but i get mybean.update() not found.
How can i call update method ?
ui:param is for templating purposes.
You don't need it here.
Just use correct composite interface and implementation
<cc:interface>
<cc:attribute required="true" name="daobean" />
</cc:interface>
<cc:implementation>
<p:commandButton action="#{cc.attributes.daobean.update}" />
</cc:implementation>
Then reference it from parent page like
<util:mycomponent daobean="#{mybean}" />