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?
Related
I have a JSF application in wich i'm using ui:composition/ui:include to display some elements inside a page like in the following code
template.xhtml
...
<h:body>
...
<h:panelGroup id="mainPanel">
<ui:include src="#{myBean.page}"/>
</h:panelGroup>
...
</h:body>
items-list.xhtml
<ui:composition>
...
<h:form>
<p:dataTable var="item" value="#{myBean.items}">
<p:column>
<h:commandLink action="#{myBean.loadItemDetail(item)}"
process="#this"
update="mainPanel">
<h:outputText value="#{item.name}"/>
</h:commandLink>
</p:column>
</p:dataTable>
</h:form>
...
</ui:composition>
items-detail.xhtml
<ui:composition>
<h:form>
<!-- Some code to display and modify the item details-->
</h:form>
<ui:composition>
MyBean.java
#ManagedBean
#ViewScoped
public class MyBean {
private String page;
private List<Item> items;
private ItemDetail itmDetail;
#PostConstruct
private void init() {
page = "items-list.xhtml"
items = ... //some logic to populate the items list
}
public void loadItemDetail(Item item){
itmDetail = ... //some logic to get the item's detail
page = "item-detail.xhtml"
}
}
The first time I click on an item to see the details it works fine but after that, if I click on the browser's back button and try to load a different item details it keeps showing me the details of the first item.
I check the JSF lifeCycle for each call and although in every call the application goes through all the satages, only the first one calls the bean method locadItemDetail. is there a reason why my method is being ignored after the first success call? is there some kind of cache on JSF where my data is bean taken from instead of my Bean?
Also i tried to avoid this behavior implementing a filter as is suggest in this post and it kind of works but now when I click browser's back button it show me an annoying page preventing me from resend the form; is there a way to prevent that?
Note:
I know that i could change the app to use GET method instead of POST to avoid this cache problem but it will required some serious changes over the app so that's not an option.
Any help or guidance about why could this be happening will be appreciated.
I am novice for JSF and I have a problem with my very simple Facelets view:
<!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"
xmlns:ui="http://java.sun.com/jsf/facelets"
xmlns:f="http://java.sun.com/jsf/core" >
<h:head>
<title></title>
</h:head>
<h:body>
<h:form id="form">
<h:panelGrid id="ciccio" columns="2">
<h:outputText value="Nome " />
<h:inputText value="#{thinBean.nome}" />
<h:commandButton id="ok" value="OK" />
<h:commandButton id="vai" value="Go" rendered="#{not empty thinBean.nome}" action="Vista1" />
</h:panelGrid>
</h:form>
</h:body>
</html>
and with my simple Backing Bean.
package magazzino;
import java.io.Serializable;
import javax.faces.bean.ManagedBean;
import javax.faces.bean.RequestScoped;
#ManagedBean
#RequestScoped
public class ThinBean implements Serializable {
private String nome;
public String getNome() {
return nome;
}
public void setNome(String nome) {
this.nome = nome;
}
}
After first call, only first button appears.
When I entered test on the field nome and submit this form by clicking button identified by ok, also second button appears.
When I click on second button, identified by vai, nothing happen: Vista1 is not rendered.
I don't understand this behavior: why Invoke Application phase is skipped?
Thanks
The rendered attribute of an UICommand component is also evaluated during apply request values phase, as part of safeguard against tampered/hacked requests wherein hackers try to invoke actions of UICommand components which are not rendered for non-admin users, for example.
In your particular case, you're using a request scoped bean. Thus, the bean is destroyed by end of every request and recreated in beginning of every request. Submitting the form by the first button counts as one request. Submitting the form by the second button counts as another request.
The UIInput values are set as managed bean property during update model values phase, which is after the apply request values phase. Thus, when the second button is pressed, the request scoped bean is newly created with all properties set to default (null). During apply request values phase, the rendered attribute is checking the input value, but it isn't been set yet and the bean is new and empty. So it won't consider the button as rendered and skip the decoding of the action event.
If you put the bean in the view scope, then it'll work for the very simple reason that the view scoped bean instance will live as long as you're interacting with the same view by returning null or void.
import javax.faces.bean.ManagedBean;
import javax.faces.bean.ViewScoped;
#ManagedBean
#ViewScoped
An alternative is checking the raw HTTP request parameter in the rendered attribute instead of the model value.
<h:inputText binding="#{nome}" value="#{thinBean.nome}" />
<h:commandButton id="ok" value="OK" />
<h:commandButton id="vai" value="Go" rendered="#{not empty param[nome.clientId]}" action="Vista1" />
See also:
How to choose the right bean scope?
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 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...
I have the following XHTML:
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:ui="http://java.sun.com/jsf/facelets"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:f="http://java.sun.com/jsf/core">
<head>
<title>TODO supply a title</title>
</head>
<body>
<f:metadata>
<f:viewParam id="productCV" name="productName" value="#{productBean.product}"
converter="#{productConverter}" required="true"/>
</f:metadata>
<ui:composition template="/templates/mastertemplate.xhtml">
<!-- Define the page title for this page-->
<ui:define name="pageTitle">
<h:outputFormat value="#{msgs.productPageTitle}">
<f:param value="#{productBean.product.description}"/>
</h:outputFormat>
</ui:define>
<!-- Pass the categoryName parameter to the sidebar so the category of this product is highlighted-->
<ui:param name="categoryName" value="#{productBean.product.categoryName}"/>
<ui:define name="content">
<!-- If productconversion failed, show this error-->
<h:message id="error" for="productCV" style="color: #0081c2;" rendered="#{productBean.product == null}" />
<!-- If productconversion succeeded show the product page-->
<h:panelGroup rendered="#{productBean.product != null}">
<p>#{productBean.product.description} #{productBean.product.categoryName}</p>
<h:form>
<h:commandLink action="#{cartBean.addItemToCart(productBean.product)}">
<f:ajax event="action" render=":cart :cartPrice" />
<h:graphicImage value="resources/img/addToCart.gif"/>
</h:commandLink>
</h:form>
</h:panelGroup>
</ui:define>
</ui:composition>
</body>
</html>
At the top I accept a String as GET param which I run through a converter and then get a Product object, I place this in the productBean.product, that bean has a setter and getter for the Product attribute, that's all.
I then use this object to show info etc. this works fine. I also add commandLink to add it to my cart using AJAX. This refuses to work if my ProductBean is in RequestScope, when I put it in SessionScope it works, but will only add the product 1 time.
As best I know this should be a straight forward RequestScope, I don't understand why it does work with SessionScope.
I have read through this post but I don't think I'm violating any of those rules.
For completeness, this is my ProductBean:
import be.kdg.shop.model.stock.Product;
import java.util.logging.Logger;
import javax.enterprise.context.RequestScoped;
import javax.inject.Named;
#Named
#RequestScoped
public class ProductBean {
private static final Logger logger = Logger.getLogger(ProductBean.class.getName());
private Product product;
public ProductBean() {}
public Product getProduct() {
return product;
}
public void setProduct(Product product) {
this.product = product;
}
}
Your bean is request scoped. So the bean instance lives as long as a single HTTP request-response cycle.
When the page with the form is requested for the first time, a new bean instance is created which receives a concrete product property as view parameter. After generating and sending the associated response, the bean instance is garbaged, because it's the end of the request.
When the form is submitted, effectively a new HTTP request is fired and thus a new bean instance is created with all properties set to default, including the product property. This way #{productBean.product} is null for the entire request. The rendered attribute of a parent component of the command link will evaluate false. The command link action is therefore never decoded. This matches point 5 of commandButton/commandLink/ajax action/listener method not invoked or input value not updated which you already found, but apparently didn't really understood.
The solution is to put the bean in the view scope. A view scoped bean lives as long as you're interacting (submitting/postbacking) with the same JSF view. Standard JSF offers #ViewScoped for this. As you're using CDI instead of JSF to manage beans, your best bet is the CDI #ConversationScoped. This is relatively clumsy (you've to start and end the scope yourself), so some CDI extension such as MyFaces CODI which offers a #ViewAccessScoped may be more useful.
See also:
How to choose the right bean scope?