Parameterizing managed bean instance as composite component attribute - jsf

I've been searched how to parametrize the Managed Bean class, and until now nothing.
What I'm doing?
I have a JSF component that access some methods from my Bean, but this methods are already implemented by an abstract Class.
The name of the methods and attributes don't change, but now I'm copying and pasting, changing only the Bean name.
The first implementation is work, but I would like to improve with the second one.
1- Component (Now):
...
<cc:interface>
<cc:attribute name="title" type="java.lang.String" required="true"/>
<cc:attribute name="data" type="java.util.List" required="true"/>
<cc:attribute name="columnModel" type="java.util.List" required="true"/>
<cc:attribute name="newEntity" type="java.lang.Object" required="true"/>
<cc:attribute name="crudState" type="java.lang.Integer" required="true"/>
</cc:interface>
...
<cc:implementation>
<p:dataTable var="entity" value="#{cc.attrs.data}" >
<p:columns value="#{cc.attrs.columnModel}"
var="column" style="#{column.style}"
styleClass="#{column.styleClass}">
...
</p:columns>
</p:dataTable>
</cc:implementation>
...
1- Implementations (Now):
...
<comp:crud
title="#{cfgUserBean.title}"
data="#{cfgUserBean.data}"
columnModel="#{cfgUserBean.coluuns}"
newEntity="#{cfgUserBean.newEntity}"
newEntity="#{cfgUserBean.crudState}"/>
...
<comp:crud
title="#{cfgCityBean.title}"
data="#{cfgCityBean.data}"
columnModel="#{cfgCityBean.columns}"
newEntity="#{cfgCityBean.newEntity}"
crudState="#{cfgCityBean.curdState}"/>
Desired:
Pass the Bean Name as a Parameter
2- Component (Desired):
...
<cc:interface>
<cc:attribute name="BEANNAME" type="java.lang.Object" required="true"/>
</cc:interface>
...
<cc:implementation>
<p:dataTable var="entity" value="#{cc.attrs.BEANNAME.data}" >
<p:columns value="#{cc.attrs.BEANNAME.columnModel}"
var="column" style="#{column.style}"
styleClass="#{column.styleClass}">
...
</p:columns>
</p:dataTable>
</cc:implementation>
2- Implementations (Desired):
...
<comp:crud BEANNAME="cfgUserBean" />
...
<comp:crud BEANNAME="cfgCityBean" />
Conclusion
As you can see, If I could parametrize the Bean name, I would be able to simplify a lot the final coding.
Any one with any Idea what I could do?
Thank you in advance

Helped by #BalusC to clarify my ideas, and I notice that the second implementation almost answered my question.
So, that what I did:
Componet
...
<cc:interface>
<cc:attribute name="managedBean" type="company.AbstractWebCrud" required="true"/>
</cc:interface>
...
<cc:implementation>
<p:dataTable var="entity" value="#{cc.attrs.managedBean.data}" >
...
</p:dataTable>
</cc:implementation>
...
Implementation
...
<comp:crud-lista
managedBean="#{cfgUserBean}"/>
...
Conclusion
Passing the abstract class as the parameter type, I was able to access all public methods from the class. Worked like a charm!
Thank you #BalusC for attention!! :-)

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>

jsf composite component nested EL

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

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?

JSF 2 Composite Component EL type conversion error

I have a JSF Composite Component that has a EL Expression on the Interface part, code snippet below.
<cc:interface>
<cc:attribute name="label" type="java.lang.String"/>
<cc:attribute name="labelRendered" default="#{cc.attrs.label ne null}"/>
</cc:interface>
<cc:implementation>
<h:outputText rendered="#{cc.attrs.labelRendered}" value="#{cc.attrs.label}"/>
</cc:implementation>
Now my problem is that the "default="#{cc.attrs.label ne null}" is giving an error.
java.lang.IllegalArgumentException: Cannot convert /resources/cc/label.xhtml #20,85 default="#{cc.attrs.label != null}" of type class com.sun.faces.facelets.el.TagValueExpression to class java.lang.Boolean
I'm using JSF 2.0.4, EL 2.1, WAS 7
The #{cc.attrs} is only available inside <cc:implementation>.
I'd suggest to rewrite it as follows:
<cc:interface>
<cc:attribute name="label" type="java.lang.String"/>
<cc:attribute name="labelRendered" type="java.lang.Boolean" />
</cc:interface>
<cc:implementation>
<ui:param name="labelRendered" value="#{empty cc.attrs.labelRendered ? not empty cc.attrs.label : cc.attrs.labelRendered}" />
...
<h:outputText rendered="#{labelRendered}" value="#{cc.attrs.label}"/>
</cc:implementation>

Resources