Why does JSF/AJAX skip phases? - ajax

I have page with an initial search/h:datatable on top. It renders well and displays all the text units (tokens) which are available.
Leading each row of the search results with a button "view". This button rerenders the panel group textUnitGroup, which holds the details of this token. Now, that works just fine. I can click all the token's view button and the area is refreshed WITHOUT a page reload. With the input fields of that panel group (form in embeded) I can also persist new tokens. Works, too.
However, if I load token details into that detail form (form is rendered again), and I press the "save text" button, JSF phase 2, 3, 4 and 5 are skipped (which did not happen, when I save a new token and the "view" button has NOT been clicked before).
12:31:44,174 INFO [org.exadel.helper] (default task-22) L:54 BEFORE RESTORE_VIEW 1
12:31:44,180 INFO [org.exadel.helper] (default task-22) L:67 AFTER RESTORE_VIEW 1
12:31:44,181 INFO [org.exadel.helper] (default task-22) L:54 BEFORE RENDER_RESPONSE 6
12:31:44,257 INFO [org.exadel.helper] (default task-22) L:67 AFTER RENDER_RESPONSE 6
Somewhat changes when re-rendering the details form resp. panel group. But I don't understand why. There is NO error in the log. But it is certainly the cause of the JSF phase-skipping (otherwise the insertion of a new token would not work, too).
This is the xhtml file. Let me know if you need more.
<!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:f="http://java.sun.com/jsf/core"
xmlns:ui="http://java.sun.com/jsf/facelets"
xmlns:c="http://java.sun.com/jsp/jstl/core"
xmlns:mywidgets="http://java.sun.com/jsf/composite/widgets"
xmlns:fn="http://java.sun.com/jsp/jstl/functions"
xmlns:pt="http://xmlns.jcp.org/jsf/passthrough">
<ui:composition template="/templates/overviewTemplate.xhtml">
<!-- <ui:composition template="/templates/contentTemplate.xhtml"> -->
<ui:define name="title">Text Administration</ui:define>
<ui:define name="body">
<h1>Text Administration</h1>
<h:panelGroup layout="block" id="searchListGroup" rendered="true">
<h:form id="listOfTextUnits">
<h:inputText autocomplete="true" style="width: 300px"
value="#{textController.searchFilter}"
pt:placeholder="Token or text in English" pt:autofocus="true"
title="Search by token or text in English">
<f:ajax event="keyup" execute="#this" render="textTable" />
</h:inputText>
<p class="betweenSections" />
<h:dataTable id="textTable"
value="#{textController.findTextUnitsByString()}" var="textUnit"
styleClass="data-table" headerClass="table-header"
rowClasses="table-odd-row,table-even-row">
<h:column>
<!-- column header -->
<f:facet name="header">Action</f:facet>
<!-- row record -->
<h:commandButton value="view" styleClass="mini"
action="#{textController.loadTextDetail()}">
<f:param name="id" value="#{textUnit.id}" />
<f:ajax execute="#form" render="textUnitGroup" />
</h:commandButton>
</h:column>
<h:column sortBy="#{textUnit.token}"
filterExpression="#{empty filterValue or fn:startsWith(textUnit.token, filterValue)}"
filterValue="#{textController.searchFilter}">
<f:facet name="header">Token</f:facet>
<h:outputText value="#{textUnit.token}" />
</h:column>
<h:column>
<!-- column header -->
<f:facet name="header">Default Text</f:facet>
<!-- row record -->
#{textUnit.defaultText}
</h:column>
</h:dataTable>
</h:form>
</h:panelGroup>
<h:panelGroup layout="block" id="textUnitGroup"
rendered="true">
<h2>Text unit</h2>
<h:form id="textUnit">
<h:inputHidden id="id" value="#{textController.id}" />
<label for="token">Token</label>
<h:inputText id="token" value="#{textController.token}"
required="true"
requiredMessage="Please insert a token string for this new translation." />
<label for="text">Default Text</label>
<h:inputTextarea id="text" value="#{textController.text}"
required="true"
requiredMessage="Please insert a default text translation."
cols="100" />
<h:commandButton value="save text"
action="#{textController.saveText()}">
<f:ajax execute="#form" render="searchListGroup" />
</h:commandButton>
</h:form>
</h:panelGroup>
<p class="betweenSections" />
</ui:define>
</ui:composition>
</html>
Thanks for any hint
--------------------------------UPDATE-------------------------------
Assuming that something with the JavaScript code has gone broken, I did put the h:panelGroup within the h:form tags. Now that works. The drawback with this is, that the h2 tags is never rerendered erspectively shown. This is what I wanted in the next step. Hide the form and then display, when needed.
To properly rephrase the question: How could one re-render a panelgroup which contains multiple h:form whitout listing all the forms in the render attribute of the f:ajax tag? Not possible at all?
Thanks for any hint!

Related

Dynamically populate options in a selectOneMenu by <ui:repeat>

I'm trying to populate some dropdown menus in primefaces with content depending on some choices from other selections in the GUI. This is a simplified example of what I'm trying to do:
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:f="http://xmlns.jcp.org/jsf/core"
xmlns:h="http://xmlns.jcp.org/jsf/html"
xmlns:ui="http://xmlns.jcp.org/jsf/facelets"
xmlns:p="http://primefaces.org/ui"
xmlns:c="http://xmlns.jcp.org/jsp/jstl/core" >
<h:head>
<title>Test</title>
</h:head>
<h:body>
<h:form>
<c:set var="options" value="#{['1','2','3']}" />
<c:set var="currentValue" value="#{3}" />
<h:outputText value="${options}" />
<ui:repeat var="r" value="#{options}">
<h:outputText value="#{r}" />
</ui:repeat>
<c:set var="currentValue" value="#{currentValue}" />
<p:selectOneMenu id="selectValue"
value="${currentValue}"
class="pFieldSet_Template_Input200 r10">
<p:ajax event="change" />
<ui:repeat var="r" value="#{options}">
<f:selectItem itemLabel="Choice #{r} (20180101)" itemValue="#{r}" />
</ui:repeat>
</p:selectOneMenu>
</h:form>
</h:body>
</html>
When I visit the page it shows [1, 2, 3]123 and an empty selectOneMenu. I would have expected the selectOneMenu to contain the choices as well. The iteration obivously works in the above case so I don't know why it doesn't show the options in the menu. What am I doing wrong?
The <ui:repeat> is an UI component while <f:selectItem> is a taghandler (like JSTL). Taghandlers runs during view build time before UI components which runs during view render time. So at the moment the <ui:repeat> runs, there is no means of a <f:selectItem>.
A <c:forEach>, which is also a tag handler, would work:
<p:selectOneMenu id="selectValue"
value="${currentValue}"
class="pFieldSet_Template_Input200 r10">
<p:ajax event="change" />
<c:forEach items="#{options}" var="r">
<f:selectItem itemLabel="Choice #{r} (20180101)" itemValue="#{r}" />
</c:forEach>
</p:selectOneMenu>

Updating dataTable data after clicking on Search button

Once page xyz.xhtml gets loaded, I am displaying 10 records.
<h:dataTable var="c" value="#{userServiceBean.showTop10Applicant()}"
styleClass="order-table"
headerClass="order-table-header"
rowClasses="order-table-odd-row,order-table-even-row"
border="1" id="appListDataTable" width="100%">
<h:column>
<f:facet name="header">
Applicant Name
</f:facet>
#{c.displayName}
</h:column>
</dataTable>
In showTop10Applicant() I'm returning 10 random records.
Above this dataTable, I have search textbox and search button. I have used ajax for checking the data.
<h:inputText id="searchApplicant" value="#{userDataBean.toSearch}" /> +
<h:commandButton value="Search" action="#{userServiceBean.searchApplicantsByCriteria()}">
<f:ajax execute="searchApplicant" render=":appListDataTable"/>
</h:commandButton>
In searchApplicantsByCriteria() I am displaying all records that I have (right now I am not checking for any criteria).
BUT, when I click on Search, I get same 10 records back as earlier.
Could anyone help where I am getting wrong.
Update 1
Below is what I want to do.
I want to search some persons that are there in database. When the page gets loaded, I am displaying random 10 persons using dataTable (using method showTop10Applicant()).
In Search textbox, I write abc and click on Search button. What I want is that the dataTable that is already present (where we are displaying 10 random records) get updated with the finding of abc. Lets say there are 2 person whose name contains abc, then I should get only list of these two persons.
Hope I am clear now. Let me know incase if there are any questions.
Update 2
<?xml version='1.0' encoding='UTF-8' ?>
<!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:ui="http://java.sun.com/jsf/facelets"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:f="http://java.sun.com/jsf/core">
<h:body>
<ui:composition template="../layout/homeLayout.xhtml">
<ui:define name="content">
<center>
<h2>
<u>
<h:outputText value="Manage Applicants"></h:outputText>
</u>
</h2>
</center>
<br />
<h:form id="myForm002" prependId="false">
<div align="right">
<h:inputText id="searchApplicant" value="#{userDataBean.toSearch}" /> +
<h:selectOneMenu value="#{userDataBean.status}">
<f:selectItem itemValue="ALL" itemLabel="All" />
<f:selectItem itemValue="A" itemLabel="Active" />
<f:selectItem itemValue="IA" itemLabel="Inactive" />
</h:selectOneMenu>
<h:commandButton value="Search" action="#{userServiceBean.searchApplicantsByCriteria()}">
<f:ajax execute="searchApplicant" render=":myForm001:appListDataTable"/>
</h:commandButton>
</div>
</h:form>
<hr width="100%"></hr>
<br />
<center>
<h:form id="myForm001" prependId="false">
<f:metadata>
<f:event listener="#{userServiceBean.showTop10Applicant()}" type="preRenderView" />
</f:metadata>
<h:dataTable var="c" value="#{userServiceBean.showTop10Applicant()}"
styleClass="order-table"
headerClass="order-table-header"
rowClasses="order-table-odd-row,order-table-even-row"
border="1" id="appListDataTable" width="100%">
<h:column>
<f:facet name="header">
Applicant Name
</f:facet>
#{c.displayName}
</h:column>
<h:column>
<f:facet name="header">
User ID
</f:facet>
#{c.userId}
</h:column>
<h:column>
<f:facet name="header">
Email ID
</f:facet>
#{c.email}
</h:column>
<h:column>
<f:facet name="header">
Mobile Number
</f:facet>
#{c.contactReference}
</h:column>
<h:column>
<f:facet name="header">
Active?
</f:facet>
<h:selectBooleanCheckbox value="#{c.isActive}"></h:selectBooleanCheckbox>
</h:column>
</h:dataTable>
<br />
</h:form>
</center>
</ui:define>
</ui:composition>
</h:body>
</html>
You need to wrap the datatable in a panelGroup like <h:panelGroup id="dataTableGroup"> and then call <f:ajax execute="searchApplicant" render="dataTableGroup"/>. This should work.
You are updating the wrong component. The ":" is the document root and you certainly have a form tag wrapped around your dataTable (that's why I asked for the form). So at least you should use something like this:
render=":formId:appListDataTable"
Replace formId with your real form id. This assumes that there are no other naming containers in between like h:panelGroup etc.
If both (the input field and the table) are inside the same naming container you could omit the colon:
render="appListDataTable"
Furthermore there might be other sources of error. I would suspect your backing bean methods (as commented by Daniel, that's why I asked for the bean code).

Ajax-render a table which is inside a different form

I am facing a problem rendering a data table when selecting a date from <rich:calendar>. I use <a4j:ajax> for the rendering but with no effect. Here is the code sample:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<ui:composition xmlns="http://www.w3.org/1999/xhtml"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:f="http://java.sun.com/jsf/core"
xmlns:ui="http://java.sun.com/jsf/facelets"
xmlns:rich="http://richfaces.org/rich"
xmlns:a4j="http://richfaces.org/a4j"
xmlns:composite="http://java.sun.com/jsf/composite">
<rich:panel header="#{lang.reportPanelHeader}" id="panel" rendered="#{navigation.reportRendered}" width="700px" style="margin-left:250px">
<a4j:status onstart="#{rich:component('statPane')}.show()" onstop="#{rich:component('statPane')}.hide()" />
<h:form id="data_table_form">
<rich:dataTable value="#{validateReportAction.reportList}" var="report" iterationStatusVar="it" id="data_table" rows="5">
<rich:column>
<f:facet name="header">#</f:facet>
#{it.index + 1}
</rich:column>
<rich:column>
....
</rich:column>
<f:facet name="footer">
<rich:dataScroller page="#{validateReportAction.page}" />
</f:facet>
</rich:dataTable>
</h:form>
<rich:popupPanel id="statPane" autosized="true" style="border: none; background-color: #e6e6e6;">
....
</rich:popupPanel>
<div id="bottom">
<h:form id="calendarForm">
<div id="left">
<div class="input" id="test_cal">
<rich:calendar
dataModel="#{calendarModel}"
value="#{validateReportAction.selectedDate}"
boundaryDatesMode="scroll"
required="true"
requiredMessage="#{lang.dateRequiredMsg}"
mode="ajax"
id="date"
datePattern="dd.MM.yyyy"
popup="false">
<a4j:ajax event="change" render="#all"/>
</rich:calendar>
<span class="error_msg">
<rich:message for="date" ajaxRendered="true"/>
</span>
</div>
</div>
</h:form>
</div>
</rich:panel>
</ui:composition>
I am forced to use #all in the calendar in <a4j:ajax event="change" render="#all"/> but I want to render only the data table. How can I do this?
Since it's located in a different naming container parent, you need to refer it by its absolute client ID. To find it out, open the page in the webbrowser. Rightclick and View Source. Locate the HTML <table> element which is generated by <rich:dataTable id="data_table">. It'll look something like this:
<table id="data_table_form:data_table">
You need to take exactly that ID, prefix with the JSF naming container separator character (which defaults to :) and use it in the render attribute.
<a4j:ajax event="change" render=":data_table_form:data_table" />
See also:
How to find out client ID of component for ajax update/render? Cannot find component with expression "foo" referenced from "bar"

how to do partial page rendering (center content) with jsf2 templating via ajax (from menu)

i have been struggling getting this to work for 2 weeks, I am trying to merge info from BalusC from these 2 posts to (link1 link2) to achieve partial page rendering of a center content area via a menu on the left. So the links on the left would update the center content via partial page rendering using <f:ajax> or maybe <a4j:ajax> via richfaces
------------------------------------------------------
| include1 link | |
| include2 link | center content like include1.xhtml |
| include3 link | |
------------------------------------------------------
my page-layout.xhtml (master template to be used by include1.xhtml, include2.xhtml,..)looks like this.
<h:body>
<div id="top" class="top">
<ui:insert name="top">page-layout default top content</ui:insert>
</div>
<div>
<div id="left">
<h:form id="navigationFormId">
<f:ajax render=":contentForm">
<h:commandLink value="include1"
action="#{navigationBean.setPage('include1')}" />
<p></p>
<h:commandLink value="include2"
action="#{navigationBean.setPage('include2')}" />
<p></p>
<h:commandLink value="include3"
action="#{navigationBean.setPage('include3')}" />
<p></p>
</f:ajax>
</h:form>
</div>
<div id="content">
<ui:insert name="content">
<h:panelGroup id="contentPanelGroup" layout="block">
<h:form id="contentForm">
<h:panelGroup rendered="#{navigationBean.page == 'include1'}">
<ui:include src="include1.xhtml" />
</h:panelGroup>
<h:panelGroup rendered="#{navigationBean.page == 'include2'}">
<ui:include src="include2.xhtml" />
</h:panelGroup>
<h:panelGroup rendered="#{navigationBean.page == 'include3'}">
<ui:include src="include3.xhtml" />
</h:panelGroup>
</h:form>
</h:panelGroup>
</ui:insert>
</div>
</div>
</h:body>
NavigationBean.java is defined as a ViewScoped bean via CDI with #Named
public class NavigationBean implements Serializable {
public NavigationBean() {}
public String getPage() {return page;}
public void setPage(String p) {page = p;}
private String page = "include1";
}
the included pages are file like include1.xhtml and include2.xhtml and should be loaded into center content when the left menu links are clicked, here is a sample include2.xhtml
<h:body>
<ui:composition template="page-template.xhtml">
<ui:define name="content">
include2
<h:form>
form components for include2
</h:form>
</ui:define>
</ui:composition>
</h:body>
I am getting FileNotFoundException, although the stack trace does not
tell which one, but removing the
<h:panelGroup rendered=".....>
<ui:include src="..." />
</h:panelGroup>
tags removes the exception. I think this is the proper implementation to accomplish this based on 2 of BalusC posts, but just can't get it to work.
The error you are getting suggests the include...xhtml page can not be found. Where are the files located? They should be in the same directory as the page-layout.xhtml file? To simplify everything I would just test if the include works, yuo can replace this:
<h:form id="contentForm">
<h:panelGroup rendered="#{navigationBean.page == 'include1'}">
<ui:include src="include1.xhtml" />
</h:panelGroup>
<h:panelGroup rendered="#{navigationBean.page == 'include2'}">
<ui:include src="include2.xhtml" />
</h:panelGroup>
<h:panelGroup rendered="#{navigationBean.page == 'include3'}">
<ui:include src="include3.xhtml" />
</h:panelGroup>
</h:form>
with:
<h:form id="contentForm">
<ui:include src="include1.xhtml" />
</h:form>
i was able to get a design via JSF2/Ajax and richfaces4 (other implementations would be ok I assume) where a left navigation menu item, implemented via templates, is able to render the center content of a template via ajax, preventing a full page refresh, and the flicker effect it has.
my navigationmenu.xhtml code looks like this
<ui:composition>
<h:form id="navigationFormId">
<rich:collapsiblePanel id="carrierCollapsibleId" header="Links that update center content" switchType="client">
<h:commandLink id="linkId1" action="/page1" value="Update Center Content with page1">
<a4j:ajax execute="#form" render=":centerContentDiv,:centerForm" />
</h:commandLink>
<br />
<h:commandLink id="linkId2" action="/page2" value="Update Center Content with page2">
<a4j:ajax execute="#form" render=":centerContentDiv,:centerForm" />
</h:commandLink>
</rich:collapsiblePanel>
...
</h:form>
I believe the render=":centerContentDiv,:centerForm" attribute for the a4j:ajax tags can be changed to just render=":centerForm" but have not tested that. I know the both work together however. also, i tried JSF2 ajax tags with white space between the IDs and got errors, so i went to richfaces 4.x's a4j and then could use commas if no space was used.
now, in order for this to work, each pageX.xhtml file, for example page1.xhtml would need to have <h:form id="centerForm">, here is an example of page1.xhtml
<ui:composition template="/templates/uiTemplate.xhtml">
<ui:define name="center_content">
<h:form id="centerForm">
<!-- put your components in here -->
</h:form>
</ui:define>
</ui:composition>
one limitation is each view that uses the template must define a <h:form id=centerForm" ...> otherwise JSF2 will complain it can't find the render target.
my uiTemplate.xhtml file has code for the header, footer, but the pertinent part that has the navigation and center content defined is as follows
<div class="header_content">
<div id="left">
<ui:include src="navigationMenu.xhtml" />
</div>
<div id="center_content">
<!-- think this panelGroup wrapper can be -->
<!-- removed. need to test that though -->
<h:panelGroup id="centerContentDiv" layout="block">
<ui:insert name="center_content" />
</h:panelGroup>
</div>
</div>

hide component, then show it, values are not saved

I have a JSF 2.0 web app with ajaxified tabs.
To switch tabs I show one and hide the others (Either with a conditional render="#{x==y}" or with with display:none/block through conditional styleClass`ing.
In either case I get the tabs to show or hide correctly.
The problem is that the values entered in the input boxes are not retained throw a hide/show of a tab (In other words, if you enter some values in tab 1, go to tab 2 and back to tab 1, the values are not retained).
I use this <f:ajax> tag on tab selection (ignited by a hash change event)
<h:form id="processFragment" prependId="false" class="hide">
<h:inputText id="fragment" value="#{fbean.fragment}">
<f:ajax event="change" execute="contentSection" listener="#{fbean.processFragment}"
render="contentSection" />
</h:inputText>
</h:form>
<div>
<h:form id="contentSection">
<ui:insert name="content" />
</h:form>
</div>
Thanks!
The code the display the tab bar and the tab contents:
<ui:define name="content">
<f:view>
<h:panelGroup styleClass="navBar">
<ul>
<ui:repeat value="#{hello.tabList}" var="tab">
<li onclick="window.location='\##{tab.tabHash}'"><h:panelGroup
styleClass="#{hello.chose == tab.tabHash?'activeTab':''}">
<h:outputText value="#{tab.tabName}" />
</h:panelGroup>
</li>
</ui:repeat>
</ul>
</h:panelGroup>
<h:panelGroup id="contentPanels" styleClass="tabContentBar">
<c:forEach items="#{hello.tabList}" var="tab">
<h:panelGroup rendered="#{hello.chose == tab.tabHash}">
<!-- <h:panelGroup styleClass="#{hello.chose == tab.tabHash?'shownTabContents':'hiddenTabContents'}"> -->
<ui:include src="#{tab.tabFile}" />
</h:panelGroup>
</c:forEach>
</h:panelGroup>
</f:view>
</ui:define>
Finally, the contents of the specific tab:
<h:outputText value="TESTING 1" />
<h:panelGroup styleClass="formField">
<h:outputText value="Enter Value:" />
<h:inputText value="#{general.name}" />
</h:panelGroup>
UPDATE
Just noticed that even if I don't hide the tabs and press a button, the value is reset. Investigating...
The values will only be retained if you use a view scoped bean and the desired form is been submitted to the server side during (or right before) tab navigation.
As an alternative, you could render all tabs at once and use only JS/CSS to show/hide tab contents. CSS-hidden inputs will still be submitted to the server side.

Resources