JSF 2 UIRepeat. When my composite component is nested in a repeat I can access a valueExpression but not its value? - jsf

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.

Related

Preprocess/postprocess a bean action method from p:remoteCommand inside composite

I have the following scenario:
JSF composite component with complex JavaScript, which I'd like to refresh partially by JavaScript with a new values from a backing bean (backing bean of a page which uses this composite component, not backing component #FacesComponent).
I don't want to make full update because it's complex JavaScript plugin and it will unacceptably break UX.
I get values from backing component #FacesComponent by using Primefaces's <p:remoteCommand> with callback as described here Best method for passing Data from Java/JSF2 bean to Javascript/jQuery Components
I know that it's some abuse of JSF but would like to encapsulate all the functionality in a single unit and don't mess with a JAX-RS If you can advice another nice solution how to encapsulate such complex jQuery plugin (for sake of clarity we are talking about FullCalendar, I know that Primefaces has its own implementation of this component but its functionality insufficient for my requirement so, I was need to make my own implementation) which highly related on ajax callbacks with parameters you're highly welcome to share it here.
My question is how to update values in a backing component #FacesComponent from backing bean by using JavaScript? Currently I involved in the following chain of events:
calling from Javascript <p:remoteCommand> with parameters which passed to backing component #FacesComponent to be dispatched later in AjaxBehaviorEvent
JavaScript:
refreshEvents([
{name:'start', value:start.format()},
{name:'end', value:end.format()}
]);
JSF code:
<p:remoteCommand name="refreshValues" oncomplete="loadValues()" action="#{cc.refreshLocal()}" process="#this"/>
Parameters which I passed stored in a backing component by using
getStateHelper().put(...);
jQuery event dispatched from composite component by following JavaScript code:
var hiddenField = $(document.getElementById(variables.hiddenId));
hiddenField.trigger("keypress");
In composite component's overridden method public void queueEvent(FacesEvent event) I add to this AjaxBehaviorEvent property which I stored before, in a 1st step and dispatch it forward.
Dispatched event from composite component "captured" in a page where composite component nested and performed process on this component:
<p:ajax event="refreshEvent" process="#this" listener="#{bean.refreshEvents}"/>
in #{bean.refreshEvent} method I perform request to #EJB bean and load data.
On callback from step 1 called again by loadValues()
<p:remoteCommand name="loadValues" action="#{cc.getLocalData()}" oncomplete="updateValues(xhr, status, args);"/>
In a backing component's method #{cc.getLocalData()} I add a callback parameter by using:
RequestContext.getCurrentInstance().addCallbackParam("param", ...);
function updateValues(xhr, status, args) from step 5 get in args this param's values and performs actual update.
So, my general question is it possible to simplify this process and how?
Thank you.
This is indeed a little overcomplicated. In total 3 ajax requests to just perform an action and a backing component passing data forth and back via the view state.
Your primary goal appears to be able to declare a bean action method as composite component attribute which should then be invoked by a <p:remoteCommand> inside the composite and return the desired model entity based on the passed-in parameters, preferably with some pre- and post-processing.
You can use <cc:attribute method-signature> in the composite interface to declare a method expression attribute:
<cc:interface componentType="yourComposite">
<cc:attribute name="eventListener" method-signature="com.example.Entity method(java.lang.String, java.lang.String)" required="true" />
</cc:interface>
Which can be used in the template client as below:
<my:composite ... eventListener="#{bean.eventListener}" />
public Entity eventListener(String start, String end) {
// ...
return entity;
}
The composite implementation can look like this:
<cc:implementation>
...
<p:remoteCommand name="refreshEvents"
process="#this" action="#{cc.processEventListener}"
oncomplete="updateValues(xhr, status, args)" />
...
</cc:implementation>
The #{cc.processEventListener} method can obtain the eventListener attribute as a MethodExpression and invoke it as below, along with some pre- and post-processing:
public void processEventListener() {
String start = getRequestParameter("start");
String end = getRequestParameter("end");
// ...
MethodExpression method = (MethodExpression) getAttributes().get("eventListener");
Entity entity = (Entity) eventListener.invoke(getFacesContext().getELContext(), new Object[] { start, end });
// ...
addCallbackParam("param", entityAsJSON);
}
Now it's only 1 ajax request "as usual".

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.

UIComponent#getValue() obtained via binding is not available in validator of another component

I'm trying to figure out why an f:attribute tag's value isn't passed when attached to h:inputSecret tag. I'm quite new to jsf, but as far as I know attributes can be attached to any kind of component. Here is the code:
<h:inputSecret id="passw" value="#{advertAdder.userPass}"
required="true" validator="#{advertAdder.validatePasswords}">
<f:attribute name="confirmedPass" value="#{advertAdder.passConfirmator.value}"/>
</h:inputSecret>
<h:inputSecret id="passwConfirm" required="true"
binding="#{advertAdder.passConfirmator}"/>
and the method that wants to acces this attribute:
public void validatePasswords(FacesContext context, UIComponent component, Object value)
{
if (!value.equals(component.getAttributes().get("confirmedPass")))
{
FacesMessage mess = new FacesMessage("Password and it's confirmation are not the same!");
context.addMessage(component.getClientId(context), mess);
((UIInput) component).setValid(false);
}
}
In above code component.getAttributes() always returns map with only two attributes:
javax.faces.component.VIEW_LOCATION_KEY and com.sun.faces.facelets.MARK_ID.
I've added attribute tag to a h:commandButton to check it, and then everything was fine. Am I missing something or it's not possible to add an attribute to non-action tag?
I'm using Mojarra 2.0.2 and Glassfish 3.0.1.
Thanks in advance.
Input components are processed in the order as they appear in the component tree. The UIInput#getValue() is only available when the component is already been processed. Otherwise you need to use UIInput#getSubmittedValue() instead.
<f:attribute name="confirmedPass" value="#{advertAdder.passConfirmator.submittedValue}"/>
Note that this gives you the unconverted and unvalidated value back. It would make somewhat more sense to put the validator on the confirm password field instead and pass the value of the first password field along. See also JSF Validator compare to Strings for Equality and JSF doesn't support cross-field validation, is there a workaround?
Alternatively, you can also try out the OmniFaces <o:validateEqual> component. You can find a concrete example in this article.
Unrelated to the concrete problem, it's unnecessary to bind the component to the bean this way. Replace all occurrences of #{advertAdder.passConfirmator} by #{passConfirmator}. Keep the controller free of properties which are never internally used.

JSF Required=Yes not working inside a datatable?

I searched everywhere but could not find a solution to this. I am trying to used
required=yes to validate whether a value is present or not. I am using it inside inputtext.
The problem is it does not work inside a datatable. If I put the text box outside the datatable it works. I am using JSF 1.7 so I don't have the validateRequired tag from JSF 2.0.
I even used a validator class but it is still not working. Does anyone know why does required=yes or validator='validationClass' inside a inputtext inside a datatable is not working.
I appreciate the help.
Thanks.
First of all, the proper attribute values of the required attribute are the boolean values true or false, not a string value of Yes. It's an attribute which accepts a boolean expression.
The following are proper usage examples:
<h:inputText required="true" />
<h:inputText required="#{bean.booleanValue}" />
<h:inputText required="#{bean.stringValue == 'Yes'}" />
As to the problem that it doesn't work inside a <h:dataTable>, that can happen when the datamodel is not been preserved properly (the datamodel is whatever the table retrieves in its value attribute). That can in turn happen when the managed bean is request scoped and doesn't prepare the datamodel during its (post)construction which causes that the datamodel is null or empty while JSF is about to gather, convert and validate the submitted values.
You need to ensure that the datamodel is exactly the same during the apply request values phase of the form submit request as it was during the render response phase of the initial request to display the form with the table. An easy quick test is to put the bean in the session scope. If that fixes the problem, then you definitely need to rewrite the datamodel preserving logic. You could also use Tomahawk's <t:saveState> or <t:dataTable preserveDataModel="true"> to store the datamodel in the view scope (like as JSF2's new view scope is doing).
Finally, JSF 1.7 doesn't exist. Perhaps you mean JSF 1.2?

Adding JSF 2 composite component at runtime from backing bean

Edited question...
Hello,
I would like to load a .xhtml file of my composite component from a backing bean, and add it to the page dynamically. The name of the .xhtml file comes form a variable.
Ex.:
public MyBean (){
String componentFile = "myCompositeComponent.xhtml"
public String addComponentToPage(){
//how do that?...
return null;
}
}
Thank you!
That's not possible. A composite component is template-based and can only be used in views. Your best bet is to repeat exactly the JSF code which you've originally written in the composite component in the model. Better would be to create a full worthy #FacesComponent class which extends UIComponent, complete with a #FacesRenderer. True, it's a tedious and opaque job, but this way you'll end up with a component which is reuseable in both the view and the model by a single code line.
An -ugly- alternative is to place all possible components in the view and use the rendered attribute.
<my:component1 rendered="#{bean.myComponentType == 'component1'}" />
<my:component2 rendered="#{bean.myComponentType == 'component2'}" />
<my:component3 rendered="#{bean.myComponentType == 'component3'}" />
...
Wrap this if necessary in a Facelets tag file so that it can be hidden away and reused in several places.
I don't understand why do you want to add a composite component from a backing bean. I guess you want to make it visible dynamically in case of an event, but for that there is AJAX reRender.
For example you can do the following:
<h:panelGroup id="composite" rendered="#{myBean.renderComponent}">
<my:compositecomponent/>
</h:panelGroup>
The renderComponent property stores a boolean value. You can switch that value and reRender composite with for e.g. Richfaces's <a4j:commandLink>.
Hope that helps, Daniel

Resources