Request parameters and requestscoped bean usage - jsf

I would like to use a few #RequestScoped beans to make some simple business logic on my entities. In their #PostContruct method, I obtain the id of the entity from the request parameters map.
This kind of setup works well for creating streamed graphics contents, using something like the following (in this case part of a composite component):
<p:graphicImage value="#{myRequestScopedBean.picture}" >
<f:param name="entityId"
value="#{cc.attrs.value.id}" />
</p:graphicImage>
However, the <f:param> tag does not seem to work for all components. For instance, how am I supposed to pass them in a <ui:repeat>, <c:set>, or to call my bean method for any of the other attributes than value?

Related

p:inputText with p:ajax calls f:viewParam Converter

We do have a small page which is using the viewParam to convert a given Id to a concrete object. This converter is called on leaving (blur) of an inputText field, which is validated. Why? Could I rework this, so that the converter is not called every time?
This is annoying, because the converter calls the set-method for the corresponding object in the BackingBean and this bean is then null, if the page is called the first time for creating this object.
<f:metadata>
<f:viewParam name="id" value="#{bean.object}"
converter="#{objectConverter}"
converterMessage="#{msgs['converter.msg.object']}"/>
<f:viewAction action="#{bean.init}"/>
</f:metadata>
<p:inputText id="text" value="#{cc.attrs.value}"
styleClass="inputTextValidated"
required="#{cc.attrs.required}"
requiredMessage="#{cc.attrs.requiredmessage}"
label="text" validatorMessage="#{cc.attrs.msg}" title="#cc.attrs.title}"
readonly="#{cc.attrs.readOnly}">
<cc:insertChildren/>
<p:ajax update="msg_text" event="blur"/>
</p:inputText>
<p>
<p:message id="msg_text" for="text" display="msg"/>
</p>
If we do not use a converter but just the viewAction to convert the Id to the corresponding object (or create a new object if applicable), everything is fine. Is this the only/correct solution for this problem?
We do use primefaces 6.1 with CDI. The converter is a #Named and #ApplicationScoped bean implementing the Converter Interface.
Using p:fragment around the inputtext-field did not help either.
That is the nature of how JSF works. If you want to not have it do that I suggest you check out OminFaces ViewParam: http://showcase.omnifaces.org/components/viewParam
From their documentation:
Stateless mode to avoid unnecessary conversion, validation and model
updating on postbacks The standard UIViewParameter implementation
calls the model setter again after postback. This is not always
desired when being bound to a view scoped bean and can lead to
performance problems when combined with an expensive converter. To
solve this, this component by default stores the submitted value as a
component property instead of in the model (and thus in the view state
in case the binding is to a view scoped bean).
The standard UIViewParameter implementation calls the converter and
validators again on postbacks. This is not always desired when you
have e.g. a required="true", but the parameter is not retained on form
submit. You would need to retain it on every single command
link/button by . To solve this, this component doesn't call
the converter and validators again on postbacks.

One <p:dialog> for multiple beans

I have a <p:dialog> in my general layout. I have its Header attribute hardcoded at the moment.
What I want is to access it from different beans and change its Header at run-time according to my choice.
I am using it to show a loading message to the user at the moment and want to update the loading text according to the current backend processing, e.g "waiting for server's response" etc.
<p:dialog id="main-status-dialog"
widgetVar="mainStatusDialog"
modal="true"
header="Loading..."
draggable="false"
closable="false"
resizable="false"
appendToBody="true">
Now I am calling it from different JSF pages on button clicks e.g <h:link outcome="/generalInformation" value="General Information" onclick="mainStatusDialog.show()" />
It works fine but always Show me "Loading..." because I have a hardcoaded Attribute. So how can I make it dynamic? Please note that I don't want to do it only for one page or bean, but from any page it Access it, i can Change ist Header accordingly.
Thanks!
You can use a managed property with #ManagedProperty of one of your managed beans (e.g HeaderBean)and change it every time accordingly and set the header value to this value, and it whould look dynamically updated.
#ManagedProperty("#{headerBean}")
private HeaderBean headerBean;
And in your header managed bean make a String property value where you will store the value of the header:
#ManagedBean(name = "headerBean")
#RequestScoped
public class HeaderBean implements Serializable
{
private String value = null;
// getter and setter methods
And in your p:dialog:
<p:dialog id="main-status-dialog"
widgetVar="mainStatusDialog"
modal="true"
header="#{headerBean.value}"
draggable="false"
closable="false"
resizable="false"
appendToBody="true">
Take a look at the following liks to find more about it:
The BalusC Code: Communication in JSF 2.0
Injecting Managed beans in JSF 2.0
#ManagedProperty - Inject one request scoped bean into another request scoped bean
EDIT:
You can use RequestContext to update the dialog from your beans, If you take a look at Better ajax operations and callbacks in JSF with PrimeFaces you will see that:
RequestContext API enables developers to do 2 simple things. 1st you can tell what you want to update in the xhtml based on conditions in actions that you define in your Managed Beans.To update a component from serverside, you can just write:
The code you need to update the p:dialog from your managed beans is:
RequestContext.getCurrentInstance().
addPartialUpdateTarget("header_id");
You can also use update attribute in your commandLink like this:
<h:link outcome="/generalInformation" value="General Information" oncomplete="mainStatusDialog.show()" update=":main-status-dialog"/>

Setting f:setPropertyActionListener value with a f:param value

I'm trying to use the setPropertyActionListener tag to set a value in my backing bean. However, it doesn't work as I expected.
Context: userService is an instance of my backing bean, which contains an int member, reqID. This, in turn, is the key to a map of objects that belong to a class called User. I'm trying to create a page that will list all instances of User, and provide a button to visit a separate view that shows that particular User's information. To do this, I'm attempting to set userService.reqID to the id of the chosen User so it can generate a reference to that user for the next view (which is done in the call userService.toUserInfo).
If I use the xhtml snippet below:
<ui:define name="content">
<h:form>
<h:panelGrid>
<ui:repeat value="#{userService.UserList.getUserList()}" var="user">
<li>
<h:outputText value="#{user.name}" />
<h:commandButton value="View details of #{user.name}" action="#{userService.toUserInfo}">
<f:param name="id" value="#{user.id}" />
<f:setPropertyActionListener target="#{userService.reqID}" value="#{id}"/>
</h:commandButton>
</li>
</ui:repeat>
</h:panelGrid>
</h:form>
</ui:define>
The tag does not appear to evaluate id correctly and I get a Null Pointer Exception.
Earlier, I tried changing my setPropertyActionListenerTag so it read out as:
<f:setPropertyActionListener target="#{userService.reqID}" value="id"/>
which gave me an error, because the tag was sending the string "id" as opposed to the int value of the parameter.
Is there some way to force f:setPropertyActionListener to evaluate the expression under value? Or is there another tag that will allow me to do this?
Also, is ui:param used appropriately here?
The <f:param> (and <ui:param>) doesn't work that way. The <f:param> is intented to add HTTP request parameters to outcome of <h:xxxLink> and <h:xxxButton> components, and to parameterize the message format in <h:outputFormat>. The <ui:param> is intented to pass Facelet context parameters to <ui:include>, <ui:decorate> and <ui:define>. Mojarra had the bug that it also behaves like <c:set> without a scope. This is not the intented usage.
Just use <c:set> without a scope if it's absolutely necessary to "alias" a (long) EL expression.
<c:set var="id" value="#{user.id}" />
Put it outside the <h:commandLink> though. Also in this construct, it's kind of weird. It doesn't make the code better. I'd just leave out it.
<f:setPropertyActionListener ... value="#{user.id}" />
See also:
Setting ui:param conditionally
what is the scope of <ui:param> in JSF?
Defining and reusing an EL variable in JSF page
Unrelated to the concrete problem, if you're using EL 2.2 (as you're using JSF 2.2, you undoubtedly are as it requires a minimum of Servlet 3.0, which goes hand in hand with EL 2.2), then just pass it as bean action method argument without <f:setPropertyActionListener> mess. See also a.o. Invoke direct methods or methods with arguments / variables / parameters in EL and How can I pass selected row to commandLink inside dataTable?
<h:commandButton ... action="#{userService.toUserInfo(user.id)}">
On again another unrelated note, such a "View user" or "Edit user" request is usually idempotent. You'd better use <h:link> (yes, with <f:param>) for this. See also a.o. Creating master-detail pages for entities, how to link them and which bean scope to choose and How to navigate in JSF? How to make URL reflect current page (and not previous one).
Oh, that <h:panelGrid> around the <ui:repeat><li> doesn't make sense in HTML perspective. Get rid of it and use <ul> instead. See also HTMLDog HTML Beginner tutorial.

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 is the JSF behaviour, if you bind the same backing bean property to two input fields in the same form?

Is there a defined behaviour in JSF, if two input fields are bound to the same session scoped Backing Bean property.
Here is my code snippet
<h:form id="myForm">
<h:inputText id="field1" value="#{TheBackingBean.theProperty}" />
<h:inputText id="field2" value="#{TheBackingBean.theProperty}" />
<h:commandButton id="continueButton" action="#{TheBackingBean.doSomething}" />
</h:form>
My question: If field1 and field2 receive different values, what will be bound to the backing bean property? Is this even allowed?
I know this is a crude scenario. My motivation is, that we have htmlunit tests running for our application. In our JSF application we want to use a cool ajaxified custom component. This doesnt work together very well with htmlunit. So my idea was, I just put in a hidden field that binds to the same property. The unit test then fills the hidden field instead of the "real" thing.
Regards
I think this kind of code is allowed, but I am not sure of the value of theProperty after the submission. What I think is that JSF will do the following:
TheBackingBean.setTheProperty(field1.value);
TheBackingBean.setTheProperty(field2.value);
However, nothing - as far as I know - specifies the order of the setter calls. Thus, after the update values JSF phase, you will not be sure if theProperty will be equal to field1.value or field2.value.
Concerning your scenario, you say that you want to bind the same property to an inputText and an hiddenText. As the hiddenText will not submit its value, unlike the inputText, this problem will not occur. Indeed, if you have this kind of JSF code:
<h:inputText id="field1" value="#{TheBackingBean.theProperty}"/>
<h:inputHidden id="field2" value="#{TheBackingBean.theProperty}"/>
then JSF will only do:
TheBackingBean.setTheProperty(field1.value);
during the submission phase.

Resources