One <p:dialog> for multiple beans - jsf

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"/>

Related

JSF Changing language from selectMenu doesn't invoke PostConstruct

I have in the header.xhtml change language selectOneMenu. Which by choosing changes the language properly of any xhtml file. But there are few pages where the language strings are set in the Java class itself which is invoked by the Post-Construct.
Header.xhtml
<h:selectOneMenu value="#{client.language}" onchange="submit()" >
<f:selectItems value="#{client.languages()}" />
</h:selectOneMenu>
In the managed bean, I have ViewScoped and then the PostConstruct. My problem here is- after changing the language from the menu, some string set by the Java are not translated. That's because it is invoked by the PostConstruct which is not invoked when the language is changed. But when I goto that page by clicking the link, then the strings are translated. Its just that as soon as I change language the strings aren't translated.
I think the problem here is because the PostConstruct is not invoked when the language is changed. How do I invoke it?
Your question is basically about the behavior of #PostConstruct. This method will be called once after the bean has been created and after the injection of fields has happened e.g. fields decorated with #EJB and #Resource annotations.
Since you use #ViewScoped bean, then this will be created once per view. Refreshing the page will create a new view, that's why your instance of the #ViewScoped bean will be recreated and you will process the data in the desired language.
Possible solutions:
Mark the bean that handles the view as #RequestScoped.
Use proper i18n internationalization for your output messages. Do not rely on messages being constructed from your managed bean.
In my case, I would use the latter rather than the former. Also, after changing the language, the best option is to fire a new non-ajax request-response cycle to the server.
More info:
How to choose the right bean scope?
Localization in JSF, how to remember selected locale per session instead of per request/view
I have tried to do it like you did in the past with no luck either. Here is how I work around the problem (using JQuery):
<h:selectOneMenu value="#{client.language}" onchange="$(document).find('.submitBtn').click();" >
<f:selectItems value="#{client.languages()}" />
</h:selectOneMenu>
<h:commandButton style="visibility: hidden;" styleClass="jsfHidden submitBtn" action="#{yourpostconstructmethod}"/>

Setting Managed Bean attribute's value from JSF page and using it in bean's methods

I have a ViewScoped Managed Bean. In my .xhtml page I want to set bean's attribute's value and use it in methods in the same bean.
I managed to set the value from jsf page, but when i want to use it in some method the value of an attribute is not the value i have set before.
Description (xhtml):
In this form there is a command link which sets the value of an attribute. And it is working fine. Also, as command link is clicked, second form is being showed.
<h:form>
<h:commandLink value="Set" >
<f:setPropertyActionListener target="#{bean.attribute}" value="true" />
<f:ajax execute="#this" />
</h:commandLink>
</h:form>
This form executes method that uses attribute's value set before, but the value is not true, its false.
<h:form>
<h:commandButton id="submit" value="Execute" action="#{bean.execute}" />
</h:form>
Bean:
public void execute(){
if(isAttribute())
---do something---
}
The question is: Why execute() is not reading attribute's value right?
When I use one form, it's working fine. But I need them to be in separated forms.
The scope of your bean is incorrect. ViewScoped means that the minute the view is changed, the bean is discarded and re-created for the next view. So, in your case, the original data you had for the first view is lost.
I'm going to refer you to BalusC's blog:
http://balusc.blogspot.co.uk/2010/06/benefits-and-pitfalls-of-viewscoped.html
which states:
A #ViewScoped bean will live as long as you're submitting the form to the same view again and again. In other words, as long as when the action method(s) returns null or even void, the bean will be there in the next request. Once you navigate to a different view, then the bean will be trashed
I can't determine of you stay on the same page with both requests. If you do, viewScope should work even in two different forms. If you are navigating from 1 view to another, another viewScope will be created and you will loose the current one.
You could set the value in the sessionScope with java or by annotating the backingNean. But then everything in your backingBean becomes sessionScoped and that might not be needed.
You could also use a spring-like flow scope.
Example to do it with java:
public void callThisAfterFirstClick() {
Faces.setSessionAttribute(attribute, true)
}
public void callThisAfterSecondClick() {
Faces.getSessionAttribute(attribute);
}

How to use component binding in JSF right ? (request-scoped component in session scoped bean)

Mojara 2.1.21
I've updated my question based on comments. I have two situation where a component is bound to server session bean. (Additional links with information: Binding attribute causes duplicate component ID found in the view and https://stackoverflow.com/a/12512672/2692917)
Version 1:
single.xhtml:
<h:outputText value=... binding="#{mysessionbean.out}" />
java:
#SessionScoped #Named public class Mysessionbean {
UIOutput out;
//getter and setter ....
}
Version 2:
template.xhtml:
<h:outputText value=... binding="#{mysessionbean.out}"
view1.xhtml:
<ui:composition template="template.xhtml" />
view2.xhtml:
<ui:composition template="template.xhtml" />
java:
#SessionScoped #Named public class Mysessionbean {
UIOutput out;
//getter and setter ....
}
Version 1 is ok. (At least I've not encounter any errors so far). But in version 2 the duplicate id error is occured if I navigate from one page to another. Why does it happen ?
Is it safe to use (request-scoped) component (in version 1) with session scoped binding ?
Are there another use cases to consider ?
Edit:
Functional requirement 1:
I want to use Primefaces datatable in a view. I need some info from this datatable. (Such as selected row or row index). So binding the datatable helps me to retrieve this info.
Functional requirement 2:
Components binding in composite components. They will be bound to session scoped bean. (And used mainly on one page, but what if I used it on another page ?
Requirements 3
The situation as in "Version 2". Template with primefaces menu and session scoped binding. For this I've used the EL-Binding.
In JSF 2.x, unless you want to manipulate components programmatically (which is at its own also rather fishy), there is no sensible real world use case to bind components to a backing bean. For sure not if they are further not been used in the backing bean itself, or if it are solely their attributes which are been flattened out.
As to the functional requirement of getting the current row of the data table, there are much better ways listed here, How can I pass selected row to commandLink inside dataTable?, for example if your environment supports EL 2.2:
<h:dataTable value="#{bean.items}" var="item">
<h:column>
<h:commandLink value="Foo" action="#{bean.foo(item)}" />
The two last requirements are totally unclear. At least, if you're doing something like:
<x:someComponent binding="#{bean.someComponent}" />
with in bean
someComponent.setSomeAttribute(someAttribute);
someComponent.setOtherAttribute(otherAttribute);
then you should instead be doing
<x:someComponent someAttribute="#{bean.someAttribute}" otherAttribute="#{bean.otherAttribute}" />
Or, if you intend to be able to use the component somewhere else in the view like so
<h:inputText ... required="#{not empty param[bean.save.clientId]}" />
...
<h:commandButton binding="#{bean.save}" ... />
and the instance is further nowhere been used in the bean, then just get rid of the unnecessary property altogether:
<h:inputText ... required="#{not empty param[save.clientId]}" />
...
<h:commandButton binding="#{save}" ... />
If there is really, really no way for some unclear reason, then split all request scoped properties of the session scoped bean out into a separate request scoped bean which you in turn bind to form actions. The session scoped one can just be injected as a #ManagedProperty of the request scoped one.
See also:
Binding attribute causes duplicate component ID found in the view
How does the 'binding' attribute work in JSF? When and how should it be used?
We ran into a similar problem and I just want to share our solution:
Problem:
In a view there was a (extended largely customized) datatable.
<x:dataTable binding="#{bean.someSomeDataTable}" />
After navigating to another page and back we wanted the datatable to have the exact same state. Previously we solved that by binding the datatable to to backing bean. This worked fine with JSPs. With Facelets we could not do that (Duplicate ID errors). So we used the binding, but only saved/restored the state of the datatable component.
public HtmlDataTable getSomeDataTable()
{
HtmlDataTable htmlDataTable = new HtmlDataTable();
if (tableState != null)
htmlDataTable.restoreState(FacesContext.getCurrentInstance(), tableState);
return htmlDataTable;
}
public void setSomeDataTable(HtmlDataTable table)
{
tableState = table.saveState(FacesContext.getCurrentInstance());
}

How does JSF load property values of managed bean?

I am a JSF beginner. I have a question about managed bean.
Step 0:
There is a managed bean BeanA, scope is request. And BeanA instance1.propertyA = "0";
Step 1:
using ajax to change country, then in BeanA.countryChanged method, change managed bean BeanA.propertyA = "A".
<t:selectOneMenu id="Country" required="true" valueChangeListener="#{BeanA.countryChanged}">
<a4j:support event="onchange" limitToList="true" ajaxSingle="true" />
<f:selectItems value="#{BeanA.countries}" />
</t:selectOneMenu>
Step2:
submit form to do validate a text input
<h:inputText id="street" required="#{BeanA.propertyA == "A"}"
I expect that in step2 the value propertyA of BeanA instance2 should be "A" in JSF validate phase, but actually it is "0". I don't know how does JSF load BeanA instance property values to create new BeanA instance. And what should I do, the value will changed to "A"? Thanks,
The symptoms indicate that your bean is request scoped. This means that it's reconstructed on every single HTTP request. You probably didn't realize that every single ajax request also counts as a separate HTTP request. In effects, you're not reusing the same bean instance across ajax postbacks on the same view. Every time a brand new instance is been created, with all its properties set to default.
JSF 2.0, which is designed with ajax in mind, has solved it with the new view scope in the standard API.
In JSF 1.x, you need to fall back to 3rd party component libraries. In your particular case, given that you're using both Tomahawk and Ajax4jsf, you've 2 options:
Use <t:saveState>.
<t:saveState value="#{BeanA}" />
Or, use <a4j:keepAlive>.
<a4j:keepAlive beanName="BeanA" />

JSF ViewScope and Bean creation

I have a problem that i don't understand:
I request a new site. A site has a link that opens a dialog. The link is inside a form.
The dialog is not inside the form.
A reduced code example:
<p:outputPanel id="layout-center" >
<h:form>
<p:commandLink id="option_field_user_profile" actionListener="#{controllerBean.getBean('userProfileBean', component).init}" oncomplete="#{controllerBean.getBean('userProfileBean', component).show}" >
<h:outputText value="#{msg.mProfile}"/>
</p:commandLink>
</h:form>
</p:outputPanel>
<p:dialog header="#{userPreferencesBean.header}" widgetVar="#{userPreferencesBean.widgetVar}" appendToBody="#{userPreferencesBean.appendToBody}" resizable="#{userPreferencesBean.resizable}" id="#{userPreferencesBean.xhtmlId}" dynamic="#{userPreferencesBean.dynamic}" modal="#{userPreferencesBean.modal}" closable="#{userPreferenceBean.closable}">
<ui:include src="/WEB-INF/templates/modification/userPreferences.xhtml" />
</p:dialog>
UserPreferencesBean is in ViewScope. My problem is now that the #PostConstruct method from the UserPreferencesBean is called twice with the non-postback request i.e. the Bean is constructed twice although it should be the same view. If i move the dialog inside the form for testing purposes it is called once, like expected. But since the dialog has its own form this is not a solution, for sure.
When the site is loaded and I hit F5, the PostConstruct method is executed once.
Has somebody an idea?
This is caused because you referenced a view scoped bean property in the view build time attribute id of the <p:dialog>. If you fix the id to be static, or to reference a request or application scoped bean property instead, then your view scoped bean will behave as expected.
See also:
JSTL in JSF2 Facelets... makes sense? - for some background explanation on view build time and view render time; the id and binding attributes of UI components are evaluated during view build time.

Resources