JSF updating certain parts of a composite component - jsf

I want to develope a JSF composite component using PrimeFaces library.
Basically I want to update my composite component. I have read some SO questions about it (JSF updating a composite component or JSF Updating Composite Component (Primefaces)). But in this case I only want to update certain parts of the component.
Here is an example. My component should be a label/message/value-part of a <p:panelGrid /> to get rid of all the noise of the <p:column/>-tags.
<composite:interface>
<composite:attribute name="label" required="true" />
<composite:attribute name="value" required="true" />
</composite:interface>
<composite:implementation>
<p:column>
<!-- label -->
<p:outputLabel value="#{cc.attrs.label}" for="id_inputtext"/>
</p:column>
<p:column>
<!-- message -->
<p:message for="id_inputtext" />
</p:column>
<p:column>
<!-- inputtext -->
<p:inputText id="id_inputtext" value="#{cc.attrs.value}"/>
</p:column>
</composite:implementation>
To use this composite component I can simply put it in a panelgrid like so.
<p:panelGrid>
<p:row>
<mycomponent:columnSet id="c1" label="label" value="hello world"/>
<mycomponent:columnSet id="c2" label="label2" value="hello world2"/>
</p:row>
<p:row>
<mycomponent:columnSet id="c3" label="label3" value="hello world3"/>
<mycomponent:columnSet id="c4" label="label4" value="#{bean.someValue}"/>
</p:row>
</p:panelGrid>
In this case I can not surround the content of the component with an HTML container element like <div/> or <span/> like it is described in the above links. That would result in weird HTML because it would be within the generated table.
What I want to do in the example above is to update the <p:outputLabel/>, the <p:message/> and the <p:inputText/> from outside of the component. In a perfect world I want to update these three components independently from each other (but I guess that is even more complicated than updating all at once).
What I currently do to get this to work is kind of cheating. I create a <composite:attribute name="id" /> and give the three components fixed IDs based on a convention using the composite component id. That works but is pretty poor because using the composite component, one needs to know the inner implementation of the it.
Does anyone have an idea to solve this requirement in a nicer way?
<composite:interface>
<composite:attribute name="id" required="true" />
</composite:interface>
<composite:implementation>
<p:column>
<!-- label -->
<p:outputLabel id="#{cc.attrs.id}_label"/>
</p:column>
<p:column>
<!-- message -->
<p:message id="#{cc.attrs.id}_message" />
</p:column>
<p:column>
<!-- inputtext -->
<p:inputText id="#{cc.attrs.id}_value"/>
</p:column>
</composite:implementation>
EDIT
Thanks for the quick response in the comments.
As to the tag files: Indeed, I must admit that I avoided dealing with tag files because composite components are so much easier to handle, my bad.
Anyway, I just read some stuff, made a quick-and-dirty prototype, but came to the conclusion that (although it might be a good and proper way to use tag files in this label/message/input-situation) I have the same issue as with the composite component: To update the components inside the tag file I need to know the inner implementation of it (that is the same as described in my workaround).
I want to update the composite component/tag file from outside with a «single handle» and treat it as a black box.
If I could wish for a feature I want something to say «do the update» on the composite component/tag file. And within the composite component/tag file I can define which components should be updated if «do the update» is triggered. Something like three separate <div id="#{cc.clientId}"/> surrounding every component I want to update (which obviously is not possible like that).
Because I guess that this is nearly impossible, I would also be happy with a way to update a composite component/tag file as a whole, meaning to update every component within the black box.

Related

<c:choose><c:when> in JSF page does not work, seems to always evaluate false

In my JSF page I use <c:choose><c:when> tag to conditionally display content. However, it does not work as it seems to always evaluate false.
E.g.
<h:outputText value="#{pollInfo.active} -" />
<c:choose>
<c:when test="#{pollInfo.active}">
<h:outputText value="Active" />
</c:when>
<c:otherwise>
<h:outputText value="Deactive" />
</c:otherwise>
</c:choose>
There are for sure a few items with active=true, which is confirmed by <h:outputText>, but it just prints Deactive for all items. You can see the actual output in the following picture:
How is this caused and how can I solve it?
The symptoms and the screenshot suggests that the #{pollInfo} represents the currently iterated item of an iterating UI component such as <ui:repeat>, <h:dataTable>, <p:tabView>, <p:dataTable>, etc.
JSTL tags run during view build time, that moment when JSF component tree is built based on XHTML source code. The var attribute of such an iterating UI component is only available during view render time, that moment when HTML output is produced based on JSF component tree.
In other words, they don't run "in sync". The #{pollInfo} is always null during view build time.
In this particular case, you need the JSF component's rendered attribute instead.
<h:outputText value="Active" rendered="#{pollInfo.active}" />
<h:outputText value="Deactive" rendered="#{not pollInfo.active}" />
Or if you intend to conditionally render larger pieces of code, wrap all in a <ui:fragment>:
<ui:fragment rendered="#{pollInfo.active}">
<h:outputText value="Active" />
<!-- Some more components here if necessary. -->
</ui:fragment>
<ui:fragment rendered="#{not pollInfo.active}">
<h:outputText value="Deactive" />
<!-- Some more components here if necessary. -->
</ui:fragment>
Again another alternative, given that you've a pure if-else, is using the conditional operator in EL:
<h:outputText value="#{pollInfo.active ? 'Active' : 'Deactive'}" />
Additional bonus is, you end up with much less code.
See also:
JSTL in JSF2 Facelets... makes sense?

How to stop recursive composite component from including itself recursively

Is it possible to have a JSF component that has ui:repeat and inside the repeat call the same component? It's because I'm building tree of question:
<cc:interface>
<cc:attribute name="questions" required="true" />
<cc:attribute name="renderQuestions" required="true" default="true" />
</cc:interface>
<cc:implementation>
<c:if test="#{cc.attrs.renderQuestions}">
<ui:repeat value="#{cc.attrs.questions}" var="q">
<p:panelGrid columns="2">
<h:outputLabel value="#{q.question}"></h:outputLabel>
<p:selectBooleanButton onLabel="#{messages['commons.yes']}"
offLabel="#{messages['commons.no']}" onIcon="ui-icon-check"
offIcon="ui-icon-close" value="#{q.ok}">
<p:ajax update="#all"></p:ajax>
</p:selectBooleanButton>
</p:panelGrid>
<cf:question renderQuestions="#{q.ok}" questions="#{q.children}" />
</ui:repeat>
</c:if>
</cc:implementation>
Currently I'm having stackoverflow?
Use a view build time tag to stop the recursion instead of a view render time tag. The component tree is built during view build time. The rendered attribute isn't evaluated during view build time, but instead during view render time. So your construct basically keeps including itself in an infinite loop. You should already know that StackOverflowError is caused by a recursion so deep that the memory stack can't handle it anymore (usually around ~1000 iterations).
Replace <h:panelGroup rendered> by a <c:if test> and it'll work as expected. The renderQuestions can by the way be simplified by omitting the check on children. The <ui:repeat> won't render anything anyway if there are no children.
If you happen to use view scoped beans in this construct and you're using Mojarra implementation, then make sure that you upgrade to at least 2.1.18, because binding a view build time tag attribute to a view scoped bean property would in older versions break the view scope.
See also:
JSTL in JSF2 Facelets... makes sense? - explains "view build time" versus "view render time"
Based on the answer of BalusC I created a solution like this.
<composite:interface>
[..]
<composite:attribute name="level" required="true" default="0"/>
</composite:interface>
<composite:implementation>
<c:if test="#{cc.attrs.level == 0}">
<a4j:repeat value="#{cc.attrs.list}" var="subList" rendered="#{cc.attrs.list != null}">
<xx:recursiveComponent list="#{subList}" id="subComponent" level="1"/>
</a4j:repeat>
</c:if>
</composite:implementation>
The level attribute is used at build time to avoid endless recursion and the renered attribute of the a4j:repeat (or ui:repeat in your case) will be evaluated at render time.

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?

How do I conditionally render an <f:facet>?

I would like to be able to conditionally omit a footer from a PrimeFaces panel element:
<p:panel header="some text">
<f:facet name="footer">
#{message}
</f:facet>
<!-- ... -->
</p:panel>
I hoped that the rendered attribute would work:
<p:panel header="some text">
<f:facet name="footer" rendered="#{!empty message}">
#{message}
</f:facet>
<!-- ... -->
</p:panel>
But the footer is still rendered, with empty content. It appears that facet does not have the rendered attribute: http://www.jsftoolbox.com/documentation/help/12-TagReference/core/f_facet.html.
What's the right way to do this?
I was able to solve this by swapping the facet out for an attribute. To summarize:
This works
<p:panel ...>
<f:attribute name="footer" value="#{message}"/>
<!-- ... -->
</p:panel>
But this doesn't work
<p:panel footer="#{message}">
<!-- ... -->
</p:panel>
Neither does this
<p:panel ...>
<f:facet name="footer">#{message}</f:facet>
<!-- ... -->
</p:panel>
Nor this
<p:panel ...>
<f:facet name="footer">
<h:outputText value="#{message}" rendered="#{!empty message}"/>
</f:facet>
<!-- ... -->
</p:panel>
by "works" I mean:
"renders no footer — not just an empty footer — when #{message} is empty or null; otherwise, correctly renders the footer with the specified text."
PrimeFaces forum thread on this issue
You could declare a ui:param and let the template check the param while renderring.
The facet in the template could then be declared as:
<f:facet name="#{hideFooter == null or not hideFooter ? 'footer' : ''}">
#{message}
</f:facet>
Any page can then declare this param
<ui:param name='hideFooter' value='#{some rule}' />
and set the appropriate rule for the param. For any page that does not declare the param, the footer will be displayed.
Here's what I did in trying to conditionally render a facet within a composite component.
<composite:interface>
<composite:facet name="header" required="false" />
</composite:interface>
<composite:implementation>
<p:panel>
<c:if test="#{empty component.facets.header}" >
<f:facet id="#{cc.attrs.id}_default_header" name="header">
all sorts of stuff here
</f:facet>
</c:if>
<c:if test="#{not empty component.facets.header}">
<composite:insertFacet id="#{cc.attrs.id}_custom_header" name="header" />
</c:if>
<composite:insertChildren id="#{cc.attrs.id}_content"/>
</p:panel>
</composite:implementation>
This let's the user of the composite component supply the header facet if they want, and if they don't, we supply a default. Obviously, instead of providing a default, you could simply not do anything.
This mixes c:if in jsf controls, but we didn't see any adverse effects.
I have come across a similar issue with plain JSF. I am not sure how a <p:panel> is rendered, but if it is rendered as a table, you can try this:
First, declare a CSS-class like this:
.HideFooter tfoot {
display: none;
}
Then set that class conditionally on the panel:
<p:panel styleClass="#{renderFooterCondition ? null : 'HideFooter'}">
The footer is still rendered in the JSF-sense, but it is not displayed and does not take up any space in the page when viewed by the user-agent.
I successfully solved this problem using ui:fragment
<ui:fragment rendered="...Test...">
<f:facet name="footer">
...
</f:facet>
</ui:fragment>
works for example to conditionnaly render the footer of a primefaces datatable (the rendered attribute of the facet does not work).
Not sure how well this would work for your footer, but I had the same issue with a legend I was trying to conditionally render. I fixed it by using the rendered on anything inside the facet tag.
<p:fieldset>
<f:facet name="legend">
<h:outputText value="#{header1}" rendered="#{header1.exists}"/>
<h:outputText value="#{header2}" rendered="#{not header1.exists}"/>
</f:facet>
content
</p:fieldset>
I had difficulty trying c:if with my ui:repeat, so this was my solution. Not quite the same as your problem, but similar.
Facets are not intended to render HTML, which is why it doesn't have the rendered attribute. Facets add functionality to a page. The term facet is probably a poor choice of name. It's very ambiguous.
..if the list compiled by ITworld's Phil Johnson has it right, the
single hardest thing developers do is name things.
ie.
JSF facets
A project facet is a specific unit of functionality that you can add to a project when that functionality is required. When a project facet is added to a project, it can add natures, builders, classpath entries, and resources to a project, depending on the characteristics of the particular project. JSF facets define the characteristics of your JSF enabled web application. The JSF facets specify the requirements and constraints that apply to your JSF project.
The JSF facets supply a set behaviors and capabilities to your web application.
This is a counter-answer to the answer from Ludovic Pénet.
This worked for me in <f:facet name="footer"> in selected p:column items of a p:dataTable (Primefaces 5.3):
...
Note how I have the ui:fragment inside the f:facet, not outside (not wrapping) it. It definitely completely removes the entire row when every footer facet is tested to NOT render (as far as I can tell, independent of the content within the ui:fragment).
Try with this, from primefaces web page
<p:columnGroup type="footer">
<p:row>
<p:column colspan="3" style="text-align:right" footerText="Totals:" />
<p:column footerText="your value in ajax" />
<p:column footerText="your value in ajax" />
</p:row>
</p:columnGroup>
clik here, to view primefaces' webpage
For those who landed here trying to hide the footer, instead of header, but the syntax component.facets.footer didn't work, should try this:
<p:panel id="panelContent">
<c:if test="#{not empty cc.facets.footer}">
<f:facet name="footer" height="100%">
your content
</f:facet>
</c:if>
</panel>
Why don't you enclose the content of the footer into a panelGroup which has the rendered attribute?
This way:
<p:panel header="some text">
<f:facet name="footer">
<h:panelGroup rendered="#{!empty message}">
#{message}
</h:panelGroup>
</f:facet>
<!-- ... -->
</p:panel>
I do it in my weapp and it works, no footer is rendered.
I don't use primefaces though, I do it with h:datatable, but I think that it must works with p:panel too.
I try this solution and ok. (http://www.coderanch.com/t/431222/JSF/java/dynamically-set-panel-header-condition)
<rich:dataGrid value="#{myMB.student.list}" rendered="#{!empty myMB.student and !empty myMB.student.list}" var="st" rowKeyVar="row">
<rich:panel>
<f:facet name="header">
<h:panelGroup id="panelHeader">
<h:outputText value="Just one student" id="header1" required="true" rendered="#{!myMB.manyStudents}"/>
<h:outputText value="#{row+1}º Student" id="header2" required="true" rendered="#{myMB.manyStudents}"/>
</h:panelGroup>
</f:facet>
<rich:panel>

jsf 2 composite component problem when use f:facet

I am new to JSF, so I have many problems with it. I have solved much, but now I have a problem when I make composite component of column.
This is the code:
myPage.xhtml:
<h:dataTable >
<util:myCol />
</h:dataTable>
myCol.xhtml:
<composite:interface>
</composite:interface>
<composite:implementation>
<h:column>
<f:facet name="header" >
<h:outputText value="user name" />
</f:facet>
<h:outputText value="some data" />
</h:column>
</composite:implementation>
The problem is that the column does not render.
So I have changed a little in code:
myPage.xhtml:
<h:dataTable >
<h:column>
<util:myCol />
</h:column>
</h:dataTable>
myCol.xhtml:
<composite:interface>
</composite:interface>
<composite:implementation>
<f:facet name="header" >
<h:outputText value="user name" />
</f:facet>
<h:outputText value="some data" />
</composite:implementation>
Here the column renders, but the header "user name" does not appear.
How to solve the problem? Thanks in advance.
Case 1:
dataTable only treats column control children as columns. You are adding a composite control to the dataTable and a the column to the composite control.
Case 2:
The problem is probably to do with where the facets are set. These are set on a map on the parent control. The control you are adding the header facet to is the composite control, not the column.
Note: the links are to JSF 1.2 (JEE5) stuff, but the principle still applies.
I don't know if you still follow this thread, but we had problems inserting facets due to a bug.
Starting the content inside the facet with a comment (server side comment < ! - - - - > ) may solve the problem showing the content. We encountered problems with facets having one liners in there.. putting an extra comment as the first statement is the workaround for the problem.
Kind regards,
Gijs
Does it render correctly when you have all of the JSF on one page? Setting up your JSF on a single page at first can be helpful to make sure that the basics are right; after that works, you can break it up into composite components.
Also, I have noticed (using JSF and Richfaces) that sometimes you can't separate 'parents' and 'children' at times; a rich:toolBar and it's rich:toolBarGroup's need to be on the same actual page, for example. I haven't worked with Facelets much so you might have a different situation.
Best of luck.
Edit:
Try pulling the header out of the composite control, i.e.:
<h:dataTable >
<h:column>
*HEADER_FACET_GOES_HERE*
<util:myCol />
</h:column>
</h:dataTable>
Use composite:facet in interface and composite:renderFacet or composite:insertFacet in implementation.
<composite:interface>
<composite:facet name="foo"/>
<composite:attribute name="value"/>
</composite:interface>
<composite:implementation>
<h:inputText id="value" value="#{cc.attrs.value}"/>
<composite:renderFacet name="foo"/>
<composite:insertChildren/>
</composite:implementation>

Resources