JSF <h:outputFormat>: use array values as parameters - jsf

On my JSF2 page, i'm using internationalized error messages.
In my backing bean, i'm putting the messages into the flash Scope:
flash.put("error", exception.getType());
On the page, this string gets translated this way:
<h:outputText value="#{bundle[flash.error]}"/>
Works fine.
NOW i want to be also able to put (an arbitrary number of) parameters into the message text, that get inserted into the placeholders in the i18n-property in my message.properties. Therefore, i'm putting the parameters as a String array into the Flash Scope, like this:
//exception.getParameters returns String[]
flash.put("errorParams", exception.getParameters())
Now i also want to be able to use this String array as parameters for an outputFormat element, to insert them into a property like Welcome, {0} {1}.
So i tried to achieve this by using ui:repeat:
<h:outputFormat value="#{bundle[flash.error]}" rendered="#{! empty flash.error}" class="invalid">
<ui:repeat value="#{flash.errorParams}" var="_param">
<f:param value="#{bundle[_param]}"/>
<!-- also doesn't work: <f:param value="#{_param}"/>-->
</ui:repeat>
</h:outputFormat>
Unfortunately, the param value is ignored and the placeholders of the i18n-property aren't replaced, so the rendered output is Welcome, {0} {1}. When using a "regular" repeater, displaying the array elements just as an outputtext, it works. So the outputFormat tag doesn't seem to support the use of a repeat as a child.
Damn, so close ;) Anyone knows a good way to do what i want, or is there any component library supporting something like that?

The problem here is that ui:repeat is a render-time child of h:outputFormat which it indeed doesn't support at all. You'd like to put multiple f:param elements directly as children of h:outputFormat during build time.
The c:forEach is suitable for this task. The JSTL core tags (which are already included in Facelets, so you don't need to install any extra JARs) do their job during building the view tree, right before it's JSF turn to process/render the view tree.
<html xmlns:c="http://java.sun.com/jsp/jstl/core">
...
<h:outputFormat value="#{bundle[flash.error]}" rendered="#{! empty flash.error}" class="invalid">
<c:forEach items="#{flash.errorParams}" var="_param">
<f:param value="#{bundle[_param]}"/>
</c:forEach>
</h:outputFormat>

Related

I can't use varStatus value to get index in p:repeat [duplicate]

I'm trying to assign an id to a component inside a <ui:repeat> like that:
<ui:repeat value="#{bean.columns}" var="column">
<h:panelGroup layout="block" id="column_#{column.id}"
styleClass="#{column.id} dashboard_column">
The thing is that #{column.id} value is being placed properly inside the styleClass value but its not being set inside the id attribute. All that is being set inside the id attribute is the automatically generated id by the JSF + my hard coded value column_.
If I remove the hard coded column_ I get an exception:
java.lang.IllegalArgumentException: component identifier must not be a zero-length String
at
Any Ideas?
This is not possible with a render-time tag such as <ui:repeat>. The <ui:repeat> will however by itself already ensure the uniqueness of the generated client ID by prepending it with the row index. So just remove the EL part from the ID attribute of the component.
<ui:repeat value="#{bean.columns}" var="column">
<h:panelGroup layout="block" id="column">
With a view build time tag such as <c:forEach> (which will basically generate multiple <h:panelGroup> components instead of only one which is rendered multiple times), it is possible to specify a dynamic ID like that.
<c:forEach items="#{bean.columns}" var="column">
<h:panelGroup layout="block" id="column_#{column.id}">
(you should only be well aware of how JSTL works in Facelets)
An alternative is to use a static <div> element instead of a JSF <h:panelGroup layout="block"> component.
<ui:repeat value="#{bean.columns}" var="column">
<div id="column_#{column.id}">
See also:
JSTL in JSF2 Facelets... makes sense?
JSF prefixes the id automatically. If you simply write id="column" the generated HTML will contain such identifiers:
myForm:0:column
myForm:1:column
myForm:2:column
and so on.
Anyway: Do never use JSTL tags (like c:foreach and c:if) in JSF templates. They cause random behaviour, very difficult to debug. And if they work, the slow down the application a lot.
Use ui:repeat for loops, and ui:fragment for conditional blocks. Note that there is no replacement for c:set, such a construct does not exist anymore in JSF 2.

Dynamic id for primefaces datatable [duplicate]

I'm trying to assign an id to a component inside a <ui:repeat> like that:
<ui:repeat value="#{bean.columns}" var="column">
<h:panelGroup layout="block" id="column_#{column.id}"
styleClass="#{column.id} dashboard_column">
The thing is that #{column.id} value is being placed properly inside the styleClass value but its not being set inside the id attribute. All that is being set inside the id attribute is the automatically generated id by the JSF + my hard coded value column_.
If I remove the hard coded column_ I get an exception:
java.lang.IllegalArgumentException: component identifier must not be a zero-length String
at
Any Ideas?
This is not possible with a render-time tag such as <ui:repeat>. The <ui:repeat> will however by itself already ensure the uniqueness of the generated client ID by prepending it with the row index. So just remove the EL part from the ID attribute of the component.
<ui:repeat value="#{bean.columns}" var="column">
<h:panelGroup layout="block" id="column">
With a view build time tag such as <c:forEach> (which will basically generate multiple <h:panelGroup> components instead of only one which is rendered multiple times), it is possible to specify a dynamic ID like that.
<c:forEach items="#{bean.columns}" var="column">
<h:panelGroup layout="block" id="column_#{column.id}">
(you should only be well aware of how JSTL works in Facelets)
An alternative is to use a static <div> element instead of a JSF <h:panelGroup layout="block"> component.
<ui:repeat value="#{bean.columns}" var="column">
<div id="column_#{column.id}">
See also:
JSTL in JSF2 Facelets... makes sense?
JSF prefixes the id automatically. If you simply write id="column" the generated HTML will contain such identifiers:
myForm:0:column
myForm:1:column
myForm:2:column
and so on.
Anyway: Do never use JSTL tags (like c:foreach and c:if) in JSF templates. They cause random behaviour, very difficult to debug. And if they work, the slow down the application a lot.
Use ui:repeat for loops, and ui:fragment for conditional blocks. Note that there is no replacement for c:set, such a construct does not exist anymore in JSF 2.

Turn a facelet tag into composite component

Some time ago, based on one of #BalusC answers (really don't know which one of them) I wrote a facelet tag that is a kind of a button generator, that generates a variable number of buttons disposed side-by-side, each one with some parameters passed through the attributes.
Now I want to turn the facelet tag into a composite component (because on the latter, I have the cc:interface tag, that provides information to others developers who wants to use the custom component).
In the rewriting process I must deal with the actions attributes of the dynamically generated buttons. In the facelet tag approach I don't have to declare them as methods, so I can pass their names as simple strings and process them inside the facelet tag also as simple strings and call the action methods. In the composite component approach it seems that I must pass the methods names as actual methods names (by using the method-signature attribute of cc:attribute). The problem is: as I don't know previously the number of buttons (it generates the buttons dinamically), I'm not able to declare the action methods in the cc:interface section.
A simplified version of my working facelet tag is:
<c:set var="idValues" value="#{fn:split(ids,',')}" />
<c:set var="actionValues" value="#{fn:split(actions,',')}" />
<c:set var="numberOfButtons" value="#{fn:length(idValues)}" />
<h:panelGrid id="#{id}" columns="#{numberOfButtons}">
<c:forEach begin="0" end="#{numberOfButtons-1}" varStatus="i">
<p:commandButton id="#{idValues[i.index]}"
action="#{bean[actionValues[i.index]]}" />
</c:forEach>
</h:panelGrid>
and the facelet tag can be used like this:
<my:button id="idButton"
ids="btnSave,btnCancel"
actions="onSave,onCancel"
bean="#{myBean}" />
[EDIT]
As sugested by #BalusC in the answers, adding code like below in the taglib.xml file was enough for the Eclipse autocompletion to work fine:
<tag>
<tag-name>button</tag-name>
<source>tags/bottons.xhtml</source>
<attribute><name>id</name></attribute>
<attribute><name>ids</name></attribute>
<attribute><name>actions</name></attribute>
<attribute><name>bean</name></attribute>
</tag>

How can I set id of a component/tag inside ui:repeat

I'm trying to assign an id to a component inside a <ui:repeat> like that:
<ui:repeat value="#{bean.columns}" var="column">
<h:panelGroup layout="block" id="column_#{column.id}"
styleClass="#{column.id} dashboard_column">
The thing is that #{column.id} value is being placed properly inside the styleClass value but its not being set inside the id attribute. All that is being set inside the id attribute is the automatically generated id by the JSF + my hard coded value column_.
If I remove the hard coded column_ I get an exception:
java.lang.IllegalArgumentException: component identifier must not be a zero-length String
at
Any Ideas?
This is not possible with a render-time tag such as <ui:repeat>. The <ui:repeat> will however by itself already ensure the uniqueness of the generated client ID by prepending it with the row index. So just remove the EL part from the ID attribute of the component.
<ui:repeat value="#{bean.columns}" var="column">
<h:panelGroup layout="block" id="column">
With a view build time tag such as <c:forEach> (which will basically generate multiple <h:panelGroup> components instead of only one which is rendered multiple times), it is possible to specify a dynamic ID like that.
<c:forEach items="#{bean.columns}" var="column">
<h:panelGroup layout="block" id="column_#{column.id}">
(you should only be well aware of how JSTL works in Facelets)
An alternative is to use a static <div> element instead of a JSF <h:panelGroup layout="block"> component.
<ui:repeat value="#{bean.columns}" var="column">
<div id="column_#{column.id}">
See also:
JSTL in JSF2 Facelets... makes sense?
JSF prefixes the id automatically. If you simply write id="column" the generated HTML will contain such identifiers:
myForm:0:column
myForm:1:column
myForm:2:column
and so on.
Anyway: Do never use JSTL tags (like c:foreach and c:if) in JSF templates. They cause random behaviour, very difficult to debug. And if they work, the slow down the application a lot.
Use ui:repeat for loops, and ui:fragment for conditional blocks. Note that there is no replacement for c:set, such a construct does not exist anymore in JSF 2.

How to use ui:include inside a forEach and use the loop variable inside the included page?

I'm upgrading to JSF 2.0.2 inside Tomcat 7.0.12 and I have a page with a variable number of reusable widgets on it. I used to use t:aliasBean for this purpose, but it doesn't seem to work. I'm now trying the following in my Xhtml:
<c:forEach items="${viewBean.currentView.parts}" var="part">
<t:div styleClass="div#{viewBean.partNumber[part]}">
<c:forEach items="${part.widgets}" var="widget">
<f:subview id="div#{viewBean.widgetId[widget]}">
<ui:include src="widgets/#{widget.widgetPage}">
<ui:param name="widget" value="#{widget}" />
</ui:include>
</f:subview>
</c:forEach>
</t:div>
</c:forEach>
What seems to happen is the next widget from the loop is used in the previous widget's page, so I get errors unless there is only one widget.
Edit: I've tried ui:repeat - it doesn't work. I've also tried removing the ui:include, just as a sanity test; the looping works fine. Also, I'm using Spring 2.5.6.SEC01 - though it shouldn't matter.
I propose to use ui:repeat instead of c:forEach.
Why?
The most important thing to understand
about the jstl tags in Facelets is
that they do not represent components
and never become a part of the
component tree once the view has been
built. Rather, they are tags which are
actually responsible for building the
tree in the first place. Once they
have done their job they expire, are
no more, cease to be, etc etc.
Source [here][1]. I really recommend to read the entire article.
UPDATE:
Please note that I do not say that c:forEach is bad. I want to underline that mixing it with ui:repeat it's not recommended.
[1]: https://rogerkeays.com/jsf-c-foreach-vs-ui-repeat

Resources