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?
Related
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.
I'm facing a problem using OmniFaces 2.1 o:validateBean with method="validateCopy" when my inputs are composite components. With method="validateActual", it validates as expected.
JSF implementation: Mojarra 2.2.8-jbossorg-1.
My composite component source:
<composite:interface>
<composite:attribute name="target" />
<composite:attribute name="label"/>
<composite:attribute name="value" />
<composite:attribute name="required" />
<composite:attribute name="size" />
<composite:attribute name="disabled" />
<composite:attribute name="styleInput" required="false" />
<composite:editableValueHolder name="input" targets="input" />
<composite:clientBehavior name="change" event="change" targets="input" />
<composite:clientBehavior name="keypress" event="keypress" targets="input" />
</composite:interface>
<composite:implementation>
<p:outputLabel id="label" for="input" value="#{cc.attrs.label}" />
<h:panelGrid columns="3">
<p:inputText id="input" value="#{cc.attrs.value}"
style="#{cc.attrs.styleInput}" size="#{cc.attrs.size}"
disabled="#{cc.attrs.disabled}" required="#{cc.attrs.required}">
</p:inputText>
<p:message for="input" display="icon">
<p:effect type="pulsate" event="load" delay="500" />
</p:message>
</h:panelGrid>
</composite:implementation>
I tracked down to the omnifaces source and got to the source point below, in the o:validateBean:
ValueReference valueReference = getValueReference(context.getELContext(), valueExpression);
if (valueReference.getBase().equals(base)) {
operation.run((EditableValueHolder) component, valueReference);
}
In a case where I use a simple input text, the valueReference().getBase() returns my to be validated bean. In a case where I use a composite component, the valueReference().getBase() returns a reference to CompositeComponentAttributesELResolver.ExpressionEvalMap.
Is threre a way to change my composite component so that it works with o:validateBean?
I have the following JSF composite component:
<composite:interface componentType="myComp">
<composite:attribute name="input" type="java.lang.Integer" />
<composite:attribute name="output" type="java.lang.Integer" />
<composite:attribute name="action" method-signature="java.lang.String action()"/>
</composite:interface>
<composite:implementation>
<h:inputText id="input" value="#{cc.attrs.input}" />
</composite:implementation>
Assuming the composite is invoked as
<h:form id="form">
<cc:myComposite id="cc" input="#{bean.input}"
output="#{bean.output}" action="#{bean.action}" />
</h:form>
Once bean.action is invoked it sets a value in bean.output. I need to access this value in my javascript, something like this
document.getElementById('form:cc:output').value
From myComp I can set the attribute with
getAttributes().put("output", output);
But the data resides in the backing bean. Any ideas?
You need a component to submit the 'output' value to the backing bean. Use inputHidden to accomplish this:
<composite:interface componentType="myComp">
<composite:attribute name="input" type="java.lang.Integer" />
<composite:attribute name="output" type="java.lang.Integer" />
<composite:attribute name="action" method-signature="java.lang.String action()"/>
</composite:interface>
<composite:implementation>
<h:inputText id="input" value="#{cc.attrs.input}" />
<h:inputHidden id="output" value="#{cc.attrs.output}" />
</composite:implementation>
Then you'll be able to set the 'output' value using its hidden input element via javascript.
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.
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?