The <ui-repeat> component disappears after an AJAX request - ajax

I use JSF with PrimeFaces framework
I have a <ui-repeat> containing <p:fieldset>, when I filter <p:fieldset> with an AJAX query, the <ui-repeat> disappears from the page.
Here is my code :
<h:form id="myForm1">
<p:panelGrid id="pnlg">
<ui:repeat var="state" value="#{myBean.listState}" varStatus="status">
<p:fieldset id="fieldsetState">
<p:dataGrid id="dataGridState" value="#{myBean.state}">
<p:panel id="pnl">
// some code...
</p:panel>
</p:dataGrid>
</p:fieldset>
</ui:repeat>
</p:panelGrid>
</h:form>
<h:form id="myForm2">
<p:commandButton value="Filter">
// When I run this AJAX query, it is supposed to update 'myForm'. Instead, 'pnlg' disappears
<p:ajax listener="#{ManageState.filterState}" update="myForm1" />
</p:commandButton>
</h:form>
When I run this AJAX query, it is supposed to update 'myForm'. Instead, 'pnlg' disappears. I think it is because of <ui:repeat>.
Here is my backing Bean :
#ManagedBean
#ViewScoped
public class ManageState extends AbstractManagedBean implements Serializable {
public void filterState() {
// Filter the list...
}
}
#ManagedBean
#SessionScoped
public class myBean implements Serializable {
private List<sortedBean> listState;
// getter, setter...
}

I solved the problem, merged the two forms and it works. But I do not know exactly why he behaves this way.

Related

Primefaces AJAX Cannot render nested UIComponent AND actionListener is not working

EDIT 2: Why it is different
#BalusC, this is different from the post you say because I'm asking about an actionListener weird behavior that's not executing a method.
Also, In my test I'm following the Primefaces's documentation, applying fixed id attribute to every NamingContainer and checking the generated HTML (and has the expected id's)
The post you say it's a duplicate from this, was a good help but here we're talking about another problems.
I was working in a webapp's layout and found a problem updating the DOM. I'm working with Primefaces 5.2 and Glassfish 4.1. To isolate the problem I tried to test it in other project but I found another issue in my test. Maybe I'm missing something, so I need to ask in this forum.
I'll go backwards: I'll describe the test and then the real problem so you can see it has the same structure
THE FAILING TEST
index.xhtml
<h:body>
<h:form id="form-render">
<p:commandButton id="btnRender" value="Render" update=":form-rendered"
actionListener="#{renderView.setRender(true)}"/>
</h:form>
<h:form id="form-rendered">
<h:outputText value="Rendered in form-rendered" rendered="#{renderView.render}"/>
<p:panelGrid id="panel-grid" rendered="#{renderView.render}">
<p:outputLabel value="Rendered in form-rendered:panel-grid"/>
</p:panelGrid>
</h:form>
</h:body>
renderView.java
import java.io.Serializable;
import javax.faces.bean.ManagedBean;
import javax.faces.bean.ViewScoped;
#ManagedBean
#ViewScoped
public class RenderView implements Serializable {
private static final long serialVersionUID = -1687524798440117276L;
private boolean render;
public RenderView() {
this.render = false;
System.out.println("Constructor - render=" + this.render);
}
public boolean isRender() {
return render;
}
public void setRender(boolean render) {
this.render = render;
System.out.println("Setter - render=" + this.render);
}
}
But when I run the test only renders the <h:outputText> component. I tried another options:
update=":form-rendered, :form-rendered:panel-grid": same result
update=":form-rendered:panel-grid": doesn't render anything
move rendered=#{...} attribute from <p:panelGrid> to <p:outputLabel> inside it: doesn't change
I don't know how to reference the <p:panelGrid> inside the <h:form>. According to Primefaces's documentation:
Update attribute takes a comma or white-space separated list of JSF component ids to be updated
So the first option should work, but it doesn't. What am I missing here?
THE REAL PROBLEM
The previous test came from a problem I was facing working with a layout. The layout's idea is the following:
In several pages I need to search something and show the results under the search form (with AJAX, of course).
Every page in the layout is a facelet template client (made with <ui:composition>)
This is ONLY the layout, there are not any queries, business logic or anything like that. The #ManagedBean classes I use are just creating dummy objects to populate the Primefaces's components
So I used the same structure of the previous test to solve the problem in every page. The funny thing is that I coded the first one and worked perfectly, but when I coded the second, fails on rendering the second form. Even more, I coded the third and worked perfectly, coded the fourth and fails!!!.
In my first solution attempt I used the following bean:
#ManagedBean // from javax.faces.bean
#ViewScoped // idem
public class SearchView {
private boolean showResults;
public SearchView() {
this.showResults = false;
System.out.println("Constructor-showResults=" + this.showResults);
}
public boolean isShowResults() {
return showResults;
}
public void setShowResults(boolean showResults) {
this.showResults = showResults;
System.out.println("Setter-showResults=" + this.showResults);
}
}
I'll copy-paste from a working example first and then form a failing example, so maybe you can see something I'm missing.
searchPage1.xhtml (working)
<ui:composition xmlns:ui="http://xmlns.jcp.org/jsf/facelets"
template="./../templateMenu.xhtml"
xmlns:h="http://xmlns.jcp.org/jsf/html"
xmlns:p="http://primefaces.org/ui"
xmlns:c="http://xmlns.jcp.org/jsp/jstl/core">
<ui:define name="left">
<ui:include src="include/auMenu.xhtml"/>
</ui:define>
<ui:define name="content">
<h1>Search 1</h1>
<h:form id="form-busq-au">
<p:panelGrid columns="2" styleClass="ui-noborder">
<!--form fields -->
<p:commandButton value="Search" icon="ui-icon-search" style="width: 100%;"
actionListener="#{searchView.setShowResults(true)}"
update=":form-res-au"/>
</p:panelGrid>
</h:form>
<h:form id="form-res-au">
<c:if test="#{searchView.showResults}">
<h1>Search Results</h1>
</c:if>
<p:panelGrid id="panelgrid-res-au" columns="2" rendered="#{searchView.showResults}">
<!-- other fields -->
</p:panelGrid>
</h:form>
</ui:define>
</ui:composition>
searchPage2.xhtml (not working)
<ui:composition xmlns:ui="http://xmlns.jcp.org/jsf/facelets"
template="./../templateMenu.xhtml"
xmlns:h="http://xmlns.jcp.org/jsf/html"
xmlns:p="http://primefaces.org/ui"
xmlns:f="http://xmlns.jcp.org/jsf/core"
xmlns:c="http://xmlns.jcp.org/jsp/jstl/core">
<ui:define name="left">
<ui:include src="include/menu.xhtml"/>
</ui:define>
<ui:define name="content">
<h:form id="form-busq-mt">
<h1>Search 2</h1>
<p:panelGrid columns="4" styleClass="ui-noborder">
<!-- form fields -->
<p:commandButton value="Search" icon="ui-icon-search" style="width: 100%;"
actionListener="#{searchView.setShowResults(true)}"
update=":form-res-mt"/>
</p:panelGrid>
</h:form>
<h:form id="form-res-mt">
<c:if test="#{searchView.showResults}">
<h1>Search Results</h1>
</c:if>
<p:panelGrid columns="2" rendered="#{searchView.showResults}">
<!-- Other fields -->
</p:panelGrid>
</h:form>
</ui:define>
</ui:composition>
As you can see, it's the same structure in the test and the two pages, but the behavior is different. I don't know if this is a bug in primefaces or what.
Debugging the code I realized that in the second page, the setSearchResult(true) method is never called, don't know why. So I tried the following solution:
Failed solution attempt
Thinking that it was a problem with the SearchView Managed Bean, I made a change in the Controller: I made an abstract class with the attribute and methods and a #ManagedBean for each one of the web pages extending the abstract class. In code:
public abstract class SearchView {
private boolean showResults;
public SearchView() {
this.showResults = false;
System.out.println("Constructor-showResults=" + this.showResults);
System.out.println("Class: " + this.getClass().toString());
System.out.println("ID: " + this.getClass().hashCode());
}
public boolean isShowResults() {
return showResults;
}
public void setShowResults(boolean showResults) {
this.showResults = showResults;
System.out.println("Setter-showResults=" + this.showResults);
}
}
And for every search page:
import java.io.Serializable;
import javax.faces.bean.ManagedBean;
import javax.faces.bean.ViewScoped;
#ManagedBean
#ViewScoped
public class ConcreteXXXSearchView extends SearchView implements Serializable {
private static final long serialVersionUID = -4717041016001640528L;
}
but the result is the same (setShowResult(true) is never called in the second page). The println() methods in the abstract class SearchView was to test that I'm instantiating different objects (indeed, there are different).
I tried the test's options but the problem is in the <p:commandButton> because actionListener is not working.
Summarizing
I'm facing two problems here:
In the test I need to render a <h:form> containing a UIComponent that implements NamingContainer(<p:panelGrid>)
In the project I need to know why the setShowResults(true) is not called in the failing page
Any help / guide is appreciated. Thanks in advance
EDIT: more info
In both TEST and REAL PROBLEM I checked the DOM with the browser inspector and the id attribute is correct for both <h:form id="form-res-mt"> and <p:panelGrid id="panelgrid-res-mt">
You have used actionListener="#{searchView.setShowResults()}" in your non-working approach.
<p:commandButton value="Search" icon="ui-icon-search" style="width: 100%;"
actionListener="#{searchView.setShowResults()}"
update="form-res-mt"/>
But, searchView.setShowResults() method expects a boolean argument, change your code to:
<p:commandButton value="Search" icon="ui-icon-search" style="width: 100%;"
actionListener="#{searchView.setShowResults(true)}"
update="form-res-mt"/>
Also, use <ui:fragment rendered="yourCondition"> instead of <c:if test="condition"> because if component condition returns false ,it'll not attached to the component tree. And thus can't find the component in the UI Tree.

JSF page only loads data when backing bean is on session scoped

This is my admin_order_search.xhtml page and it has a <p:dataTable>
and I'm trying to call a admin_view_order.xhtml page with the particulate
order object
<p:column>
<f:facet name="header">
<h:outputText value="View" />
</f:facet>
<p:commandButton action="admin_view_order?faces-redirect=true" id="view" value="View Info" update="dataTable" actionListener="#{viewOrderController.loadOrder(order)}" />
</p:column>
and this is my "admin_view_order.xhtml"
<h:form id="orderViewForm">
<p:growl id="growl" showDetail="true" autoUpdate="true" sticky="false" />
<p:outputLabel value="#{viewOrderController.order.orderId}"></p:outputLabel>
<p:outputLabel value="#{viewOrderController.order.customerName}"> </p:outputLabel>
</h:form>
and hear is my "ViewOrderController.java" page
#Component
#ManagedBean
#ViewScoped
public class ViewOrderController implements Serializable{
private static final long serialVersionUID = 1L;
private Order order;
public void loadOrder(Order order){
System.out.println("ID : "+order.getOrderId());
System.out.println("Name : "+order.getCustomerName());
this.order = order;
}
public Order getOrder() {
return order;
}
public void setOrder(Order order) {
this.order = order;
}
}
the problem is admin_view_order.xhtml shows the order details, only if my ViewOrderController is on #SessionScoped but I want it to be on ViewScoped
Please let me knows how to do that?
Why do you want the Controller to be ViewScoped?
After leaving the view (navigate to admin_view_order.xhtml) a new instance of your controller is used. So the changes you made before (setting the order in loadOrder) are gone.
There is another way to do that , is to passing parameter through the GET request ( for example you can pass the id of the order object in admin_order_search.xhtml to , and then recover this id and use it in your backing bean by searching the object with this id ).
This is possible by using view parameters
1-pass the id of the object (add the folowing tag inside your button )
<f:param name="id" value="#{viewOrderController.order.orderId}" />
2-After that, in your admin_view_order.xhtml page, catch the view param and load the info for that order:
<f:metadata>
<f:viewParam name="id" value="#{viewOrderController.newOrderId}" />
<f:event type="preRenderView" listener="#{viewOrderController.loadOrder}"/>
</f:metadata>
note that i added an attribut ( newOrderId ) to put the order.orderId into it

jsf navigation including content pages via ajax

i have a not resolveable problem, or at least with my limited knowledge about jsf.
I know there are some good solutions to find on stackoverflow, but i can't figure out my error.
i just want to have some commandlinks like a navigationbar and they should change the content of a pre defined div tag which got an include clause. So i guess my Index could be reinterpreted as a kind of template.
my Index:
<h:panelGroup id="navigation" layout="block">
<h:form>
<h:panelGrid columns="4" columnClasses="colDefault,colDefault,colDefault,colDefault">
<f:ajax render=":include">
<h:commandLink value="entry1" action="#{menuController.setPage('login')}" />
<h:commandLink value="entry2" action="#{menuController.setPage('register')}" />
<h:commandLink value="entry3" action="#{menuController.setPage('welcome')}" />
</f:ajax>
</h:panelGrid>
</h:form>
</h:panelGroup>
<h:panelGroup id="center_content" layout="block" class="center_content" >
<h:panelGroup id="include">
<ui:include src="#{menuController.page}.xhtml" />
</h:panelGroup>
</h:panelGroup>
its just like in this post of BalusC
with a small and pretty simple bean:
#ManagedBean
public class MenuController implements Serializable{
private String page;
public String getPage() {
return page;
}
public void setPage(String page) {
this.page = page;
}
}
but i got a TagAttributeException #
/index.xhtml #17,92 action="#{menuController.setPage('login')}" Could not Resolve Variable [Overflow]: menuController
i've tryed, but i have no clue what to do.
You need to put the bean in a fixed scope:
#ManagedBean
#ViewScoped
public class MenuController implements Serializable {}
And you need to preinitialize page with a default value:
private String page;
#PostConstruct
public void init() {
page = "login"; // Default value.
}

nested jsf listener not called in #ViewScope

Currently I have a problem similar to
jsf listener not called inside nested ui:repeat
After I got the solution of this problem I upgraded Mojarra to Version 2.1.16.
Everthing works fine, up to the moment I was implementing the below-mentioned view.
The Code is simplified to the basics.
xhtml:
<form>
<div>
// listener is called
<h:commandLink value="test1">
<f:ajax event="click" listener="#{testBean.testListener}" />
</h:commandLink>
<ui:repeat var="testList" value="#{testBean.testList}">
// listener is not called !
<h:commandLink value="test2">
<f:ajax event="click" listener="#{testBean.testListener}" />
</h:commandLink>
</ui:repeat>
</div>
</form>
bean:
#ManagedBean(name="testBean")
#ViewScoped
public class TestBean {
private Collection<TestObj> testList;
public Collection<TestObj> getTestList() {
return this.testList;
}
// the listener
public void testListener() {
...
}
}
Even if I change ui:repeat to c:forEach, the problem is the same!
Just if I use #SessionScope instead of #ViewScope the 2nd commandLink (inside the iteration) calls the listener. But I'd like to avoid #SessionScope at this bean.
Do you have any hints for me about the reason of this problem and how to handle it?

JSF ajax commandbutton from multiple forms within same page reinitiliazes the viewscoped bean

changeThe page has 2 forms. In the first form, the ajax-button initializes an attribute of the viewscoped managed bean. In the second form, the ajax-button uses that attribute to do some stuff. Problem is that the method called by the second form button never gets invoked. Instead, the viewscoped bean is re-initialized and when I hit the same button again, of course the attribute is null resulting in NPE.When I put the 2 buttons within the same form, then everything works as expected.Is this normal: should ajax enabled buttons always reside within the same form?
<h:form id="frmDetail">
<h:commandButton id="btn_changePlant" title="#{msg.ttChangePlant}" immediate="true" image="/resources/img/edit.png">
<f:ajax event="click" render=":fsDetailPlant :headerMsg" listener="{assortiment.detailPlantId}"/>
</h:commandButton>
</h:form>
...some other code ...
<h:form id="frmOffers">
<h:commandButton id="btn_offers" title="#{msg.ttOfferPlant}" immediate="true" value="#{msg.btn_offers}">
<f:ajax event="click" render=":fsDetailPlant :headerMsg" listener="{assortiment.changeOffers}"/>
</h:commandButton>
</h:form>
and the managed bean looks like
#ManagedBean
#ViewScoped
public class Assortiment extends BeanObject implements Serializable {
....
public void detailPlantId(AjaxBehaviorEvent actionEvent) {
clear();
plantdetail = facade.findPlantdetail(plantdetailsIdModel.getRowData());
}
public void changeOffers(AjaxBehaviorEvent actionEvent) {
System.out.println("........ Assortiment.changeOffers(AjaxBehaviorEvent actionEvent)");
if (containerItems.isEmpty()) {
SessieUtils.fillPropertyItems(containerItems, PlantProperty.codeValuesList(ECodeType.logistic, "L02"), facade.getLocale());
}
//
if (offersModel == null) {
offersModel = new ListDataModel<OffersDto>(plantdetail.getOffersList());
}
}
It might just be that the listener attributes need the # prefix,
listener="{assortiment.changeOffers}"
should be
listener="#{assortiment.changeOffers}"

Resources