jsf composite component nested EL - jsf

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}" />

Related

Mojarra-Bug while resolving #{cc} when passed as include parameter and used by included composite?

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.

How to define the content of repeater inside composite through its interface?

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>

How to pass a validator to a inputtext in a nested(!) composite component in JSF 2

I have the following problem with nested composite components and passing validators to a inputtext in a nested component:
The wrapper custom component (wrapper.xhtml):
<cc:interface>
<cc:attribute name="value" required="false" />
</cc:interface>
<cc:implementation>
<h:panelGroup>
<h:outputLabel value="TEST:"/>
<temptest:input value="#{cc.attrs.value}">
<cc:insertChildren/>
</temptest:input>
</h:panelGroup>
</cc:implementation>
The nested custom component (input.xhtml):
<cc:interface>
<cc:attribute name="value" required="false" />
<cc:editableValueHolder name="input" targets="input" />
</cc:interface>
<cc:implementation>
<h:inputText value="#{cc.attrs.value}" id="input" >
<cc:insertChildren/>
</h:inputText>
</cc:implementation>
The trial to pass a validator to the nested custom component (pageXYZ.xhtml):
<h:form>
...
<temptest:wrapper value="#{bean.value}">
<f:validateRequired for="input"/>
</temptest:wrapper>
<!-- this works:
<temptest:input value="#{bean.value}">
<f:validateRequired for="input"/>
</temptest:input> -->
<h:message for="input"/>
...
</h:form>
Is there a way to pass (one or more) validators to a nested custom component?

a composite component inside panelgrid is not 'expanded'

Basically, i've been using this pattern again and again inside a panelgrid :
<h:panelGrid columns="2" >
<h:outputLabel for="heroName" value="Hero Name : " />
<h:panelGroup>
<h:inputText label="Hero Name : "
value="#{ccBean.data.map['heroName']}" id="heroName" />
<p:message for="heroName" />
</h:panelGroup>
...
</h:panelGrid>
I notice that i duplicate things so much, like the id in h:inputText, which will be duplicated again in for="idValue" for outputLabel, and then again in p:message. The same thing happens with the inputText's label value and h:outputLabel value.
So, i was thinking about making a simple composite component to wrap these up :
<composite:interface>
<composite:attribute name="id" required="true" />
<composite:attribute name="label" required="true" />
<composite:attribute name="value" required="true" />
<composite:attribute name="includeLabel" required="false" default="false"/>
</composite:interface>
<composite:implementation>
<h:outputLabel for="#{cc.attrs.id}" value="#{cc.attrs.label}"
render="#{cc.attrs.includeLabel}" />
<h:panelGroup>
<h:inputText label="#{cc.attrs.label}"
value="#{cc.attrs.value}" id="#{cc.attrs.id}" />
<p:message for="#{cc.attrs.id}" />
</h:panelGroup>
</composite:implementation>
And then i make use of the composite components :
<h:panelGrid columns="2" >
<s:inputText
id="heroName"
label="Hero Name : "
value="#{ccBean.data.map['heroName']}"
includeLabel="true"
/>
<s:inputText
id="heroName2"
label="Hero Name 2 : "
value="#{ccBean.data.map['heroName2']}"
includeLabel="true"
/>
...
</h:panelGrid>
That composite component works fine, but the panelGrid now assumes that the s:inputText is one component. Before using the composite component, there are 2 components in one row, which is the outputLabel and the panelGroup.
Is it possible to hint the composite component to 'expand' itself in this case so that it takes up 2 columns instead of 1 column in my panelGrid ?
Thank you !
No, unfortunately not. Your best bet is to create a simple tag file instead.
See also:
How to make a grid of JSF composite component?

JSF 2.0: h:inputText inside composite component fails with non-String objects when validation is set

In a backing bean:
#Min(3)
Integer foo;
If I have form like:
<h:form>
<h:commandButton value="Submit" />
<h:inputText value="#{bean.foo}" />
</h:form>
This works ok. However, if I do something like
<cc:interface>
<cc:attribute name="text" />
<cc:editableValueHolder name="text" targets="field" />
<cc:interface>
<cc:implementation>
<h:inputText id="field" value="#{cc.attrs.text}" />
</cc:implementation>
and call this inside form instead of directly h:inputText as in:
<!-- <h:inputText value="#{bean.foo}" /> -->
<pref:fieldComponent text="#{bean.foo}" />
But then I get:
javax.validation.ValidationException: Unexpected exception during isValid call
at org.hibernate.validator.engine.ConstraintTree.validateSingleConstraint(ConstraintTree.java:144)
at org.hibernate.validator.engine.ConstraintTree.validateConstraints(ConstraintTree.java:118)
at org.hibernate.validator.metadata.MetaConstraint.validateConstraint(MetaConstraint.java:121)
at org.hibernate.validator.engine.ValidatorImpl.validateValueForGroup(ValidatorImpl.java:655)
...
And the root cause is:
Caused by: java.lang.ClassCastException: java.lang.String cannot be cast to java.lang.Number
at org.hibernate.validator.constraints.impl.MinValidatorForNumber.isValid(MinValidatorForNumber.java:32)
at org.hibernate.validator.engine.ConstraintTree.validateSingleConstraint(ConstraintTree.java:141)
... 69 more
If I remove validation, it works. Also, if foo is of type String, it works also with validations.
I tried playing with cc:editableValueHolder, defining different types (also omitting it) and a few other tricks but I am a bit unsure how to actually implement this. Or is it a bug? Seems like it's forgetting to use a converter? Have I misunderstood something?
As per a comment on your ticket, it turns out that you could as workaround explicitly specify the type converter.
You could do it as follows
<pref:fieldComponent text="#{bean.foo}">
<f:converter converterId="javax.faces.Integer" />
</pref:fieldComponent>
and
<cc:implementation>
<h:inputText id="field" value="#{cc.attrs.text}">
<cc:insertChildren />
</h:inputText>
</cc:implementation>
or maybe
<pref:fieldComponent text="#{bean.foo}" converter="javax.faces.Integer" />
and
<cc:implementation>
<h:inputText id="field" value="#{cc.attrs.text}" converter="#{cc.attrs.converter}" />
</cc:implementation>

Resources