Choosing between composite components inside ui:repeat - jsf

I have something like <ui:repeat value="#{list}" var="a" /> where list is of type List<Action> and contains various objects that extend Action. Depending on the actual type of Action, I want to render a different composite component (for example, for ActionA I want to render component A and for ActionB I want to render B, where B would throw an exception if it were to get a value of type ActionA). How can I do this? It seems that using rendered="#{a.type == 'ACTION_A'}" (where type is a property of Action) on the composite components doesn't work as each iteration of ui:repeat adds all components to the tree so it sends the value a to each composite.
Thank you.

Related

What exactly is #{component} in EL?

According to https://code.google.com/p/primefaces/issues/detail?id=4720, The ComponentUtils.resolveWidgetVar(String expression, UIComponent component) function is available in Primefaces since 2013. It can be used in EL by the "#{p:widgetVarFromContext(searchExpression, component)}" function.
This is useful in case of several components have the same id in different NamingContainer, but are still present in the same view. In this case,
the #{p:widgetVar(searchExpression)} function only returns the last one found.
I don't understand however how to reference the UIComponent that must be passed as the second argument from EL. The above mentioned bug report suggests we can refer to it using #{component}. Can anyone provide me with an example?
The #{component} is an implicit EL variable referring the current UIComponent in EL scope (see also implicit EL objects). You can usually only refer it in component's HTML attribute or in template text children.
E.g. in case of <h:inputText> it will reference an instance of UIInput class which has among others an isValid() method.
<h:inputText id="foo" required="true"
style="background: #{component.valid ? '' : 'pink'}"
onclick="alert('Client ID of this component is #{component.clientId}');" />
You can also use binding attribute to let JSF during view build time put a reference to a component instance in the Facelet scope. This way the component reference will be available anywhere in the Facelet during view render time.
<script>alert('Client ID of foo component is #{foo.clientId}');</script>
<h:inputText binding="#{foo}" />
See also:
Difference between client id generated by component.clientId and p:component()
JSF component binding without bean property
How does the 'binding' attribute work in JSF? When and how should it be used?
The p:widgetVarFromContext is useful when referring to a PrimeFaces widget inside a composite component. There could be more than one instance of your component on the same page. So writing widgetVar="expression" and PF('expression') is out of the question. There would be multiple widgets with the same name. It is then better to omit the widgetVar attribute and use the generated one which is unique because it is based on the clientId.
You can't use #{p:widgetVar('expression')} within your <cc:implementation> because it leads to a Cannot find component for expression "expression" referenced from "j_id1" error instead of the expected PF('widget_expression').
But you can use #{p:widgetVarFromContext('expression', cc)} which will return something like PF('widget_wrapperform_compositecomponent1_expression'). The cc refers to the root of the composite component instance.

Update attribute in JSF custom component expect full element ID path where as direct PrimeFaces component runs with short element ID path

I am using <p:commandButton> tag with update attribute and passing current element ID in update attribute as currentForm: messages. something like: update="currentForm: messages". Where messages is the id of <div> element which I want to update after request call.
This is working fine.
Now I created a composite component with the same command button as we have in main template and replaces that PrimeFaces command button with created custom command button. (for now no any custom changes made in created command button just prime faces command button in this)
When I rendered the template it is complaining me about the ID given update attribute error given as:
javax.faces.FacesException: Cannot find component with expression "currentForm
and if I give full element ID path for this element it works again.
Although it is good if we give full element ID path to the element, but it surprise me why it is working when I am calling direct PrimeFaces command button ?
That's because composite components are like <h:form>, <h:dataTable>, etc also naming containers. Their effect on ajax client IDs is outlined in this related question: How to find out client ID of component for ajax update/render? Cannot find component with expression "foo" referenced from "bar".
This is done so because you otherwise run into "duplicate component ID" errors on composite component's implementation when reusing the composite component more than once in the same naming container parent. See also this related question: Should a composite component really require namingcontainer interface?
If you're absolutely positive that the composite component is the right solution for the concrete requirement and thus not a tagfile (a lot of starters namely overuse composite components due to their zero-configuration nature, while it should really best only be used if you intend to bind a single model property to a bunch of closely related components), then you may consider the following alternative ways to update the messages component:
Either dynamically pass the target client ID:
<my:compositeButton ... update=":#{messages.clientId}" />
...
<p:messages binding="#{messages}" ... />
(note: the code is as is; you don't need to bind it to a bean property or so!)
Or just let PrimeFaces auto-update the messages component on every ajax request:
<my:compositeButton ... />
...
<p:messages autoUpdate="true" ... />

Set ID from backing bean in JSF

Is this legal?
<h:form id="status${a.myID}" >
// ...
</h:form>
where 'a' is an object in a backing bean. It seems to sort of work, but when I look at the rendered HTML, I see the id as: :0:status for example, instead of :status0 as I would expect. My main problem is trying to reference the id from <f:ajax render=.... I'm getting "contains an unknown id..." with pretty much every combination I can think of. Is it possible to set ids using values from a backing bean reliably?
The single-letter variable name ${a} and the symptom of an iteration index like :0 being auto-appended in client ID, suggests that this <h:form> is inside a JSF iterating component such as <h:dataTable> or <ui:repeat> with a var="a" which actually is not a backing bean. It would confirm and explain all symptoms described so far. If ${a} were a real backing bean (and not an iteration variable), then it would have "worked" and you would have seen :0:status0, :1:status0, :2:status0, etc — whose usefulness is questionable though.
First of all, the id attribute of a JSF component is evaluated and set during view build time, the moment when the JSF component tree is to be built based on XHTML source code file. The var attribue of a JSF iterating component is set during view render time, the moment when the HTML output is to be generated based on JSF component tree. Thus, logical consequence is, the object set by var attribute is not available at the moment the id attribute needs to be set and thus evaluates to null/empty-string. The effect is exactly the same as when you would do
<h:form id="status">
JSF iterating components namely already auto-appends the iteration index to the generated client ID. It would not make any sense to manually set the ID of the iterated item in the component ID. There's namely physically only one <h:form> component in the JSF component tree which is in turn reused multiple times during producing the HTML output based on the current iteration round.
This Q&A should also give more food for thought: JSTL in JSF2 Facelets... makes sense?
Coming back to your concrete functional requirement of referencing a component in <f:ajax render>, you definitely need to solve this differently. Unfortunately you didn't clearly describe the context of the source component and the target component, so it's impossible to give/explain the right client ID and so I'm just giving you a link so that you can figure it out on your own: How to find out client ID of component for ajax update/render? Cannot find component with expression "foo" referenced from "bar"
Unrelated to the concrete problem, the old JSP EL style ${...} has in Facelets exactly the same effect as #{...}. In order to avoid confusion by yourself and your future maintainers it's recommend to completely ban usage of ${...} and stick to #{...} all the time. See also Difference between JSP EL, JSF EL and Unified EL
Actually ${a.myID} this is not rendering any output. As you are getting :0:status as form ID which implies, :0 is parent of :status in HTML tree structure.

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 2 UIRepeat. When my composite component is nested in a repeat I can access a valueExpression but not its value?

I declare my repeater in a form (lets say in main.xhtml):
<!--<itemRenderer:MetaFieldRenderer item="{kueditmeta.metaFieldFirst}" >
</itemRenderer:MetaFieldRenderer>-->
<ui:repeat var="foo" value="#{kueditmeta.metaFields}" >
<itemRenderer:MetaFieldRenderer item="#{foo}" >
</itemRenderer:MetaFieldRenderer>
</ui:repeat>
My form on submit inspect my MetaFieldRenderer.xhtml composite component in the repeater, I look at an attribute: <cc:attribute name="item" /> by calling MetaFieldRenderer().getModel();
In my FacesComponent back bean I expose getModel() method:
public MetaFieldModel getModel() {
ELContext el = this.getFacesContext().getELContext();
ValueExpression valExp = this.getValueExpression("item");
Object itemValue = valExp.getValue(el);
return (MetaFieldModel) itemValue;
}
itemValue is always null when I nest MetaFieldRenderer in a UIRepeat. But if I uncomment the one outside of the repeater itemValue is not null.
Anyone knows what's going on? Is there a better way to bind from and attribute to my FacesComponent? Am I missing a necessary implementation in my component so the UIRepeat do update its children?
(I use JSF 2.0.3)
I expected the components to populate themselves at pretty much the same phase (or at least before Invoke application (aka: action, navigateTo...) phase). But UIRepeat at Invoke Application phase always contained only one child, the itemRenderer it would use for repeating, the data was not repeated yet (Ho it happen at render phase I guess?).
I came to understand something through those steps:
Restore View phase bring back a plain UIRepeat and its children declare in the xhtml. No UIRepeat doesn't populate anything yet, there is only the itemRenderer without any value in it at that point.
To see how its children (if composite component) are not bound to anything at that moment use the required attribute <cc:attribute required="true"... its gonna throw an error even if you gave the attribute because uirepeat doesn't pass them.
Lesson learned, don't do logic/validation on the composite component backbean, just reflect the model. Now I drive my repeater with a list of model that can validate itself and if the model contains a warn message or whatever fancy stuff I just display it in the composite component.

Resources