I am learning JSF 2 at the moment. One of the first things I want to realize is the following scenario:
I have a commandLink on my page. When this link is clicked, an h:panelGroup-Tag is rendered (depending on the boolean attribute in the corresponding ManagedBean - which is false by default). I want that panelGroup to not be rendered when the link which "opened" the panelGroup is clicked again (that already works) but I want the panelGroup also not to be rendered when the user clicks outside of that panelGroup (that doesn't work).
The ManagedBean looks like:
#ManagedBean
#SessionScoped
public class LocaleBean {
#PostConstruct
public void init(){
locale = new Locale("de_DE");
showLanguageDiv = false;
}
private boolean showLanguageDiv;
public boolean getShowLanguageDiv() {
return showLanguageDiv;
}
public void setShowLanguageDiv(final boolean showLanguageDiv) {
this.showLanguageDiv = showLanguageDiv;
}
public void switchShowDivStatus(ActionEvent e) {
showLanguageDiv = !showLanguageDiv;
}
The facelet looks like:
<ui:composition xmlns="http://www.w3.org/1999/xhtml"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:ui="http://java.sun.com/jsf/facelets" xmlns:f="http://java.sun.com/jsf/core">
<h:form>
<div>
<h:commandLink style="float: right;padding: 2px" actionListener="#{localeBean.switchShowDivStatus}" value="#{msg.language}" >
</h:commandLink>
</div>
<h:panelGroup rendered="#{localeBean.showLanguageDiv}" style="clear:both;float:right;border:2px;border-style: solid" >
<p><h:commandButton .... /></p>
<p><h:commandButton .... /></p>
<p><h:commandButton .... /></p>
</h:panelGroup>
</h:form>
</ui:composition>
I already tried to add an hidden link which was clicked via javascript (when the user clicked anywhere in the body), which set the boolean attribute of the ManagedBean to false so that the panelGroup was not rendered. That worked partially because the panelGroup wasn't shown anymore. But on the other hand a click on the link to show the panelGroup had no effect anymore after doing that.
This is how it looked:
<h:body onclick="document.getElementById('invisibleform:invisiblelink').click()">
.
.
.
<h:form id="invisibleform">
<h:commandLink id="invisiblelink" actionListener="#{localeBean.switchShowDivStatus}"></h:commandLink>
</h:form>
</h:body>
So how can I resolve this issue? Is there a best practice of doing things like that? Was the hidden link the right approach? From what I've read so far it seems like this is something which should be done on client side via javascript. But all solutions I found were based on normal html-pages (non-JSF) and I can't image how this should be done when using JSF.
Thanks for any advice.
Are you able to use Primefaces along JSF? There is a Component <p:overlayPanel> See here for the showcase. Maybe that works for you...
Related
I have a JSF page that loads the properties of an object (for which the id is passed in the URL). The loading can last more seconds, so I would like to display a wait/busy indicator or a "Loading..." message.
This is done using "viewAction"
<f:metadata>
<f:viewAction action="#{myBean.loadParams}" />
</f:metadata>
Is there a simple way to accomplish this goal? I'm using Primefaces.
PrimeFaces has already a component ready for that: the <p:outputPanel deferred="true">. You only need to make sure that the #{heavyBean} is only referenced in a component (and thus definitely not in a tagfile like <c:xxx> for the reasons explained here) within the <p:outputPanel> and not somewhere else.
...
#{notHeavyBean.property}
...
<p:outputPanel deferred="true">
...
#{heavyBean.property}
...
</p:outputPanel>
...
#{anotherNotHeavyBean.property}
...
Then you can do the heavy job in its #PostConstruct method. Do the job you originally did in <f:viewAction> there in the #PostConstruct.
#Named
#ViewScoped
public class HeavyBean implements Serializable {
#PostConstruct
public void init() {
// Heavy job here.
}
// ...
}
If you need to access properties of other beans, simply #Inject those beans in the HeavyBean. E.g. in case you needed the ID view param:
<f:viewParam name="id" value="#{notHeavyBean.id}" />
#Inject
private NotHeavyBean notHeavyBean; // Also #ViewScoped.
#PostConstruct
public void init() {
Long id = notHeavyBean.getId();
// Heavy job here.
}
The <p:outputPanel> already comes with an animated gif. You can easily customize it via CSS.
.ui-outputpanel-loading {
background-image: url("another.gif");
}
I would like to propose also this simple approach:
one "landing" page (the page where we first navigate in) with a wait indicator and an autoRun remoteCommand with an event that read the parameter "param" from the URL and save it in the bean.
the remoteCommand does a redirect to another page (where the long-running method loadParams is executed)
In this way the wait indicator is shown until the second page is ready to be displayed.
Do you see any weaknesses?
Here the landing page:
<!DOCTYPE html>
<html
xmlns="http://www.w3.org/1999/xhtml"
xmlns:ui="http://java.sun.com/jsf/facelets"
xmlns:f="http://java.sun.com/jsf/core"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:p="http://primefaces.org/ui">
<h:head>
...
</h:head>
<f:metadata>
<f:event type="postAddToView" listener="#{notHeavyBean.readProperty}" />
<f:viewParam name="param"/>
</f:metadata>
<h:body>
<p:outputPanel layout="block">
<i class="fa fa-circle-o-notch fa-spin layout-ajax-loader-icon" aria-hidden="true" style="font-size: 40px;position: relative;top: 50%;left: 50%;"></i>
</p:outputPanel>
<h:form>
<p:remoteCommand action="#{notHeavyBean.redirect}" autoRun="true"/>
</h:form>
</h:body>
I am seeing different behaviors of and in a page containing multiple forms.
Here is my backing bean:
import javax.annotation.PostConstruct;
import javax.faces.bean.ManagedBean;
import javax.faces.bean.ViewScoped;
#ManagedBean
#ViewScoped
public class MultiFormBean
{
String inputText1 = "";
String inputText2 = "";
#PostConstruct
public void initializeBean(){
System.out.println("PostConstruct Called ------------------");
}
public String getInputText1()
{
return inputText1;
}
public void setInputText1(String inputText1)
{
this.inputText1 = inputText1;
}
public String getInputText2()
{
return inputText2;
}
public void setInputText2(String inputText2)
{
this.inputText2 = inputText2;
}
public void doSubmit1() {
inputText2 = inputText1;
}
public void doSubmit2() {
inputText1 = inputText2;
}
}
When i use the following xhtml , clicking Submit1 and Submit2 any number of times won't call #PostConstruct more than once:
<h:body>
<h:form id="firstForm" prependId="false">
<h:panelGroup layout="block" id="renderTarget1"/>
<h:inputText id="first_input" value="#{multiFormBean.inputText1}"/>
<h:commandButton id="click1" action="#{multiFormBean.doSubmit1}" value="submit1" type="submit"
onclick="javascript:jsf.ajax.request(this, event, {execute:'firstForm', render:'renderTarget1 secondForm'}); return false;">
</h:commandButton>
</h:form>
<h:form id="secondForm" prependId="false">
<h:panelGroup layout="block" id="renderTarget2"/>
<h:inputText id="second_input" value="#{multiFormBean.inputText2}"/>
<h:commandButton id="click2" action="#{multiFormBean.doSubmit2}" value="submit2" type="submit"
onclick="javascript:jsf.ajax.request(this, event, {execute:'secondForm', render:'renderTarget2 firstForm'}); return false;">
</h:commandButton>
</h:form>
</h:body>
But the following xhtml would call #PostConstruct more than once:
<h:body>
<h:form id="firstForm" prependId="false">
<h:panelGroup layout="block" id="renderTarget1"/>
<h:inputText id="first_input" value="#{multiFormBean.inputText1}"/>
<a4j:commandButton id="click1" action="#{multiFormBean.doSubmit1}" value="submit1" type="submit" execute="#form" render="renderTarget1,secondForm"/>
</h:form>
<h:form id="secondForm" prependId="false">
<h:panelGroup layout="block" id="renderTarget2"/>
<h:inputText id="second_input" value="#{multiFormBean.inputText2}"/>
<a4j:commandButton id="click2" action="#{multiFormBean.doSubmit2}" value="submit2" type="submit" execute="#form" render="renderTarget2,firstForm"/>
</h:form>
</h:body>
Please can anyone help me use the <a4j:commandButton> instead of <h:commandButton>
Also i see that i cannot call the method doSubmit2() with a4j commandButton
I think that problem here is in bug inside JSF2 and Richfaces4. From 4 version Richfaces started using JSF embedded ajax capabilities. And There is a bug with using multiple forms on page with ajax requests. The problem there that richfaces renders special hidden input with the id of currently rendered view state. This id is changed when new view is rendered. And it is also submitted with every request to show that it belongs to some specific view. So when you have multiple forms on the same page after first ajax request the view state is getting the wrong place and it can be not submitted again second time. Sometimes behavior looks like very very wierd with no logical description.
PostConstruct is called twice because server thinks that two requests belong to different views(view state is not sumbitted) and as far as bean is view scoped it is created twice. After clicking aroung ajax can completelly stop working with this because server woukd not recognize the view(probably what you see when you can not click second submit button).
In the first place I recommend you to use latest available version of JSF and Richfaces. This bug (and many more) may be already fixed there.
This question already has answers here:
How to dynamically add JSF components
(3 answers)
Closed 6 years ago.
A click on a commandButton should trigger an action in a ManagedBean: to add a new "outputText" component to the current page.
The overall idea is to have the page changed dynamically with user action, with server side action because new elements added to the page need data from a db to be laid out.
-> How do I add a component to the page from a managed bean in jsf / primefaces? Let's say that the elements should be added in an existing div like:
<div id="placeHolder">
</div>
(this div could be changed to a jsf panel if needs be)
Note: if alternative methods are better to achieve the same effect I'd be glad to learn about them.
I'll provide you another solution apart from the one you posted. Basically it has a List of given outputs, which is increased everytime the button is pushed. That should render exactly the same DOM tree as the solution you stated:
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:ui="http://java.sun.com/jsf/facelets">
<h:head>
<title>Tiles</title>
<h:outputStylesheet name="css/320andup_cle.css" />
</h:head>
<h:body>
<h:form>
<h:commandButton actionListener="#{bean.createNewTile}" title="new"
value="new" />
</h:form>
<h:panelGroup layout="block" id="tiles">
<ui:repeat var="str" value="#{bean.strings}">
<h:panelGroup>
<h:outputText styleClass="tile" value="#{str}" />
</h:panelGroup>
</ui:repeat>
</h:panelGroup>
</h:body>
</html>
#ManagedBean
#SessionScoped
public class Bean {
List<String> strings = new ArrayList<String>();
public List<String> getStrings() {
return strings;
}
public void createNewTile() {
strings.add("output");
}
}
Apart from being much simpler IMHO, it has a main advantage: it doesn't couple your server side code to JSF implicit API. You can change the #ManagedBean annotation for #Named if you want it to be a CDI managed bean.
The solution:
This is a jsf page with a button creating a new div each time it is clicked:
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:h="http://java.sun.com/jsf/html">
<h:head>
<title>Tiles</title>
<h:outputStylesheet name="css/320andup_cle.css" />
</h:head>
<h:body>
<h:form>
<h:commandButton actionListener="#{bean.createNewTile()}" title="new" value="new"/>
</h:form>
<h:panelGroup layout="block" id="tiles">
</h:panelGroup>
</h:body>
</html>
The Managed Bean:
#Named
#SessionScoped
public class Bean implements Serializable {
private UIComponent found;
public void createNewTile() {
HtmlPanelGroup div = new HtmlPanelGroup();
div.setLayout("block");
HtmlOutputText tile = new HtmlOutputText();
tile.setValue("heeeeeRRRRRRRRRRRRRR ");
tile.setStyleClass("tile");
div.getChildren().add(tile);
doFind(FacesContext.getCurrentInstance(), "tiles");
found.getChildren().add(div);
}
private void doFind(FacesContext context, String clientId) {
FacesContext.getCurrentInstance().getViewRoot().invokeOnComponent(context, clientId, new ContextCallback() {
#Override
public void invokeContextCallback(FacesContext context,
UIComponent component) {
found = component;
}
});
}
}
See this app built with this logic of dynamically generated components: https://github.com/seinecle/Tiles
I am facing a problem with commandButton, it is working only when the type is submit. Can someone take a look and let me know if there is a solution for that? The code below is very simple and does have the the propose to illustrate what I need. The method test() is not executed. Method runSubmit is executed successfully.
I need that test method is executed without a submit as the original page does have validations that are executed during the submit, test() method must be executed without a submit as it is a preliminary operation before of the submit.
I am using PrimeFaces 4.0, JDK 7, Tomcat 6 and JSF 2.0 (Apache), however I think it is happening in Mojarra as well.
SESSION:
package com.andre.bean;
public class AndreBean {
public void runSubmit() {
System.out.println("Submit executed");
}
public String test() {
System.out.println("Not submit executed");
return "true";
}
}
XHTML
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:f="http://java.sun.com/jsf/core"
xmlns:p="http://primefaces.org/ui">
<h:head>
</h:head>
<h:form id="test">
<p:commandButton id="ns" value="not submit" type="button" action="#{andreBean.test}" ajax="false"></p:commandButton>
<p:commandButton id="s" value="submit" action="#{andreBean.runSubmit}"></p:commandButton>
</h:form>
</html>
Thank you very much
Andre
What's going on?
What you get is correct behaviour. In PrimeFaces button with type="button" works as it does in basic HTML - it doesn't cause any request. As PrimeFaces user's guide says:
Push buttons are used to execute custom javascript without causing an
ajax/non-ajax request. To create a push button set type as "button".
<p:commandButton type="button" value="Alert" onclick="alert('Prime')" />
If you want to "talk to" bean, you need to use type="submit" (which is default in p:commandButton).
However... contrary to submit buttons behaviour in HTML, in PrimeFaces such submission will not force redirection to new page but all communication will be handled by underlying AJAX requests.
Therefore only your second button will execute beans' method:
<p:commandButton id="s" value="submit" action="#{andreBean.runSubmit}" />
What probably you wanted to obtain?
If you don't want to send all your form to bean you can limit the scope of components that are processed with "process" attribute of p:commandButton:
<h:form id="test">
<p:inputText value="#{andreBean.value}"/>
<p:commandButton id="s" value="submit" action="#{andreBean.runSubmit}" process="#this" />
</h:form>
With the following bean you will see the difference:
public class AndreBean {
private String value;
public void runSubmit() {
System.out.println("Submit executed");
}
public String getValue() {
System.out.println("getValue");
return value;
}
public void setValue(String value) {
System.out.println("setValue: " + value);
this.value = value;
}
}
If you don't limit executed components in console you get:
getValue
setValue: foobar
Submit executed
...and with components limited only to process="#this" you get only:
Submit executed
Hope that helps.
Sometimes, the solution is simply add immediate="true", it changes the point, in JSF lifecyle, in which the bean action is triggered.
Here are an article about how and when use it:
immediate attribute article
Please check your binding with bean.
bean fields should be String or non primitive.
I have a dataTable. The data of the dataTable is filled via ajax. A row of the table contains among other things form elements like a button. The button in the datatTable should refer to another page but if I click on them the current page is reloaded.
Here some code:
the backing bean:
#ManagedBean(name="bean")
#SessionScoped
public class Bean {
private List<String> data;
#PostConstruct
public void postConstruct() {
data = new ArrayList<String>();
}
public void fillTable() {
data.add("E1");
data.add("E2");
data.add("E3");
}
public String outcome(){
return "/faces/test/edit.jsf";
}
public List<String> getData() {
return data;
}
public void setData(List<String> data) {
this.data = data;
}
}
the page:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org /TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:h="http://java.sun.com/jsf/html" >
<h:body>
<h:form id="form">
<h:commandButton value="fillTable">
<f:ajax listener="#{bean.fillTable()}" render="#form"/>
</h:commandButton>
<h:dataTable id="table" var="data" value="#{bean.data}">
<h:column>
<h:outputText value="#{data}" />
</h:column>
<h:column>
<h:commandButton value="edit" action="#{bean.outcome}" />
</h:column>
</h:dataTable>
</h:form>
</h:body>
</html>
I know that this has something to do that the form is already in the dom and the button are lazy loaded into the page (if someone could be more specific I would be very pleased).
Although if I change the Scope of the backing bean to SessionScope it works. The button redirect to the right page. Why?
Although if I change the Scope of the backing bean to SessionScope it works. The button redirect to the right page
The bean should have been placed in the view scope. The session scope is too broad and would only risk unintuitive behaviour when the same view is been opened in multiple browser windows/tabs in the same session.
The explanation is as follows: when a form is submitted, JSF needs to identify the command button pressed in order to invoke the associated action method. As the command button is been placed in side a datatable, JSF needs to iterate over its datamodel first. But if the datamodel has been changed, or is empty, then JSF won't be able to identify the command button. Hence the action won't be invoked.
When the bean is in the request scope, then it will be trashed by end of response and recreated during every new request, including ajax requests. All of the bean's properties will obviously get the default values again, so also the data property in your case.
See also:
commandButton/commandLink/ajax action/listener method not invoked or input value not updated - particularly point #4.
How to choose the right bean scope?