JSP Custom Tag File and Dynamic attributes - jsp-tags

I have a custom JSP tag tag file, which is a wrapper around HTML input controls - dropdown etc, like so -
<mytag:dropdown table="customer_master" style="display:block;" class="dblist"/>
Here "table" is a declared attribute of the tag where as style, class are dynamic-attributes.
Now I would like to wrap this is in another tag like so -
<mytag:search-filter type="dropdown" validate="true" table="mytable" style="display:block;" class="dblist">
I am trying to implement mytag:search-filter as follows -
<#tag .... dynamic-attributes="dynattrs">
<#attribute name="type" required="true">
<c:if test="${type == 'dropdown'}">
<mytag:dropdown table="$dynattrs['table']">
</c:if>
The outer tag has its own set of declared attributes and I want the inner tag's attributes to be passed in as dynamic attributes to the outer tag.
The outer tag will then pass its dynamic attributes to the inner tag
I have tried the following and none of them seem to work -
<c:set var="attrs">
<c:forEach items="${dynattrs}" var="a">
${a.key}="${a.value}"
</c:forEach>
</c:set>
<mytag:dropdown table="$dynattrs['table']" ${attrs}/>
The above gives a "Unterminated Tag %lt;mytag:dropdown" Error
<mytag:dropdown table="$dynattrs['table']">
<c:forEach items="${dynattrs}" var="a">
<jsp:attribute name="${a.key}" trim="true">${a.value}</jsp:attribute>
</c:forEach>
</mytag:dropdown>
** The above does not throw any error, but the attribute value inside the inner tag is always empty.
Constraints I have :
a) I want to stick to tag files and do not want to use Java class implementation of TagSupport.
b) As far as possible, do not want to change the inner tag implementation because it is widely used in its current form and any change to it means a huge effort on rework.
Is it possible to achieve what I am trying to do ?
Why do the above not work (especially the second one) ?

Related

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>

<a jsf:rendered="#{...}"> is not interpreted as passthrough element

I don't understand why this piece of code is working:
<h:link value="Login" rendered="#{sessionBean.userInSessionBean == null}" />
and this piece of code is not working:
<a jsf:rendered="#{sessionBean.userInSessionBean == null}">Login</a>
A HTML element will only become a passthrough element if following conditions are met:
There's at least one jsf:xxx attribute from http://xmlns.jcp.org/jsf namespace.
There's at least one "identifying attribute" associated with a specific JSF component.
For the <a> element an identifying attribute is necessary so JSF can decide whether to interpret it as <h:commandLink>, <h:outputLink> or <h:link>. Without an identifying attribute, JSF wouldn't have any idea what component you actually meant to use, so any jsf:xxx attributes will be ignored. The jsf:rendered is not sufficient as identifying attribute because it appears on every single JSF component, so JSF would still have no idea which one you meant.
Given that you seem to intend to have a <h:link>, then use jsf:outcome as identifying attribute.
<a jsf:outcome="login" jsf:rendered="#{empty sessionBean.userInSessionBean}">Login</a>
A completely different alternative is to wrap plain HTML in an <ui:fragment rendered>. See also How to conditionally render plain HTML elements like <div>s?

panelGroup without style or styleClass is not rendered unless ID is given

According to the tag library for http://xmlns.jcp.org/jsf/html, a h:panelGroup is
Intended for use in situations when only one
UIComponent child can be nested, such as in the case of facets.
If the "style" or "styleClass" attributes are present, and the
"layout"
attribute is present with a value of "block", render a "div"
element,
outputting the value of the "style" attribute as the value of the
"style" attribute and the value of the "styleClass" attribute as the
value of the "class" attribute. Otherwise, if the "layout" attribute
is not present, or the "layout" attribute contains a value other
than
"block", render a "span" element, outputting the value of the
"style" attribute as the value of the "style" attribute, and the
value
of the "styleClass" attribute as the value of the "class"
attribute.
In case of
<h:panelGroup id="id" layout="block">
<!-- ... --->
</h:panelGroup>
or
<h:panelGroup layout="block" style="margin-right: 10px;">
<!-- ... --->
</h:panelGroup>
a div is being rendered:
<div id="id">
</div>
respective
<div style="margin-right: 10px;">
</div>
but when omitting the id (if one don't want to update the panelGroup) or the style (if one don't want to style the panelGroup) no div is being rendered and the resulting HTML can mess up ones layout. Furthermore exploring the realm of JSF, a panelGroup can also be used to conditionally render child elements using its rendered flag but as mentioned before when omitting the two mentioned attributes the result is rendered conditionally but without a div, such as
<h:panelGroup layout="block" rendered="true">
<it>Without DIV.</it>
</h:panelGroup>
leads to
<it>Without DIV.</it>
After this inquiry I want to check with the Stackoverflow community that I understood it right that when not using a panelGroup as a naming container or to customary style its elements one is better off solving the conditional rendering part (if needed) using a ui:fragment and the layouting part with a hard-coded div. Is this so?
Note: we are talking about if h:panelgroup will render any HTML component, but unless render="false" the inner components are not blocked from rendering in any case.
The behaviour tree of h:panelgroup consists of two checks:
Is at least one of "id" or "style" or "styleClass" attributes set?
Yes:
a. Renders a <div> if layout="block", otherwise
b. Renders a <span> (layout="gibberish" or non-existant)
No:
Renders no html component. Still useful if you want to use "rendered" or when you can nest only one component (i.e. <f:facet>)
The test for the layout attribute only comes after 1., above. Since your third example goes into the no branch, it is ignored.

How to implement foreach in jsf?

How do I create ofer_has_location objects (join object from location and ofer) using the current ofer and the selected items from the h:selectManyCheckBox
<h:selectOneMenu id="companyidCompany"
value="#{oferController.selected.companyidCompany}"
title="#{bundle.CreateOferTitle_companyidCompany}"
required="true"
requiredMessage="#{bundle.CreateOferRequiredMessage_companyidCompany}">
<f:ajax event="valueChange" execute="companyidCompany"
render="locationCollection" />
<f:selectItems value="#{companyController.itemsAvailableSelectOne}"/>
</h:selectOneMenu>
<h:outputLabel value="#{bundle.CreateOferLabel_locationCollection}"
for="locationCollection" />
<h:selectManyListbox id="locationCollection" value="locations"
title="#{bundle.CreateOferTitle_locationCollection}">
<c:forEach items="locations">
<f:selectItems var="locations"
value="#{oferController.selected.companyidCompany.locationCollection}" />
</c:forEach>
</h:selectManyListbox>
What you need to do in order to achieve 'connected elements' functionality:
Have two elements ( <h:selectOneMenu> and <h:selectManyLisBox> in your case), where the second one will be dependent on the selected option(s) of the first one. The second element must have an id in order to be rerendered afterwards.
Every HTML select element (that's rendered by both JSF tags of your choice) will have a set of options that are not supposed to be created via iterative element like <c:forEach> (though, it is in fact possible), but rather via the <f:selectItem>/<f:selectItems> tags (thus, remove your iterative tag in comment).
When values in the components are bound not as plain Strings, or primitive wrappers (Integer, etc.), but rather as model objects (YourClass objects, etc.), then you need to tell JSF two things: how can it print option's value from your class and how can it reconstruct an object from request parameter that is a string. For this you need to implement Converter, that is, explain JSF how to do the abovementioned transformations. Use this answer and BalusC's blog as reference points. Note the appropriate syntax for <f:selectItems itemValue="..."> here.
Model values bound by these two components also need to represent your classes, just in a same way as selected items' values. For <h:selectOneMenu> it is value="#{}"; for <h:selectManyListbox> it is value="#{}" with YourClass selectOneMenuValue and List<YourClass> selectManyListboxValues or YourClass[] selectManyListboxValues bean properties respectively.
Population of second select will be handled via <f:ajax> tag. As contents need to be calculated 'on the fly', the right spot to make it is within its listener attribute (i.e. to have List<YourClass> contentsOfSecondListbox = createListboxValues(YourClass oneMenuSelectedOption);) . As you'd desire to rerender the second element, specify its client id in render attribute of <f:ajax>. Example here.
In case you are binding, for example, to String/String[] values, you won't need the converter parts.
Try to go through it step by step to find out your errors and correct them.

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

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>

Resources