I found a strange behaviour regarding the ajax rendering of JSF and I am not shure what I am missing.
For simplicity I have a page with an h:inputText and and a h:panelGrid with a button inside. When someone clicks the button, the value of the h:inputText should be set with a standard value and the h:panelGrid should disappear. At first I will show the code then I explain it a bit more.
xhtml page:
<h:inputText id="trackingIdValue" value="#{bean.trackingId}" />
<h:panelGrid border="15" id="panel" rendered="#{not empty bean.list}">
<h:commandButton action="#{bean.render}" value="Clear Panel" >
<f:ajax execute="#this" render="panel trackingIdValue" />
</h:commandButton>
</h:panelGrid>
corresponding bean:
private Long trackingId;
private List<String> list;
public Long getTrackingId() {
return trackingId;
}
public List<String> getList() {
return list;
}
public void setList( List<String> list ) {
this.list = list;
}
#PostConstruct
public void init() {
// the list will be filled with some initial values
}
public void render() {
this.trackingId = 954643L;
list.clear();
}
When the page gets loaded the list will be filled with some values in the PostConstruct. This leads to the displaying of the panel because the list is not empty now. But when I click the button in the panel (where I change the trackingId and empty the list) the trackingId changed in the GUI but the panel is still visible. What did I do wrong?
UPDATE 1
As written in the comment I tried the code above in an empty project and there it works. I currently have a qiete huge application so shrinked down my pages and ended the following:
My project is consists of multiple page templates.In my master layout I have a h:head tag which loads many of my stylesheets I need for the application. When I completely remove the h:head tag then it works but then of course my styling is pretty bad. What does this has to do with my ajax loading and the panelgrid?
UPDATE 2
I have shorten my application to just one tiny page:
<h:head>
<h:outputStylesheet name="masterStyle.css" library="css" />
</h:head>
<h:body>
<h:form>
<h:inputText id="trackingIdValue" value="#{testBean.trackingId}" />
<h:panelGroup border="15" id="panel"
rendered="#{not empty testBean.list}">
<h:commandButton action="#{testBean.render}" value="Clear Panel">
<f:ajax execute="#this" render="panel trackingIdValue" />
</h:commandButton>
</h:panelGroup>
</h:form>
</h:body>
</html>
When I remove the header tag the ajax request works fine with both the inputtext and the panel. But when I add my stylesheets in the header like above ajax only works with the inputtext.
Related
I have a p:accordionPanel which represents a list of items and in each tab there is a form. Upon submitting any of the repeating forms, it is possible that extra data in needed, which is when a p:dialog is popped up prompting the user to enter some more data. That dialog is defined outside the accordion panel because, unlike the items from the accordion, only one of them can be showing at a time so there is no need to augment the HTML served by repeating it in each tab of the accordion.
The definition of the accordion looks as follows (simplified but with all the relevant descriptors):
<p:accordionPanel id="myAccordion" value="#{managedBean.getAccordionList}" var="item" multiple="false" dynamic="true" cache="true">
<p:ajax event="tabChange" listener="#{managedBean.processTabChange}"/>
<p:tab title="#{item.tabTitle}" id="itemTab">
<h:form id="itemForm">
<h:outputLabel for="itemName" value="Item Name:"/>
<p:inputText id="itemName" title="Item Name:"
maxlength="16" value="#{appeal.itemName}">
</p:inputText>
Consequently, the HTML rendered for itemName is myAccordion:0:itemForm:itemName in the first instance, myAccordion:1:itemForm:itemName in the second, etc.
The dialog is defined as follows:
<p:dialog id="commentDialogID" header="Enter comment" widgetVar="commentDialog" modal="true" resizable="true" height="auto">
<h:form id="commentForm">
<h:outputLabel for="comment" value="Comment:"/>
<p:inputTextarea id="comment" title="Comment"
rows="6" cols="33"
value="#{managedBean.activeItem.comment}"
required="true">
<f:ajax render="comment"/>
</p:inputTextarea>
<h:commandButton value="Submit" action="#{managedBean.proceed}" onclick="PF('commentDialog').hide();">
<f:ajax render="*** ??? ***"/>
</h:commandButton>
</h:form>
</p:dialog>
What I have repeatedly been failing at are attempts to f:ajax update only a single tab in the accordion panel, the active one from which the dialog was popped up. I tried using
:myAccordion:#{managedBean.activeItem.displayIndex}:itemForm:itemName
:myAccordion:#{managedBean.activeItem.displayIndex}:itemForm
:myAccordion:#{managedBean.activeItem.displayIndex}:itemTab
in place of *** ??? *** but none of them would compile:
javax.faces.FacesException: <f:ajax> contains an unknown id ':myAccordion:0:itemForm' - cannot locate it in the context of the component j_idt20
If I skip the index token (e.g. :myAccordion:itemForm:itemName) it does compile but it does functionally nothing. The Item entity class has a getDisplayIndex which does accurately return the index of the active tab.
My problem is quite similar to that which is described in this question, which doesn't really have a good answer. Could it be a limitation of PrimeFaces?
I don't know what version of Primefaces you are using, but this seems to be a bug in Primefaces 5.1.1 where i could recreate this issue. By upgrading to Primefaces 5.2, the ajax EL could suddenly find the referenced id. I can post a MCVE if needed.
Edit: The MCVE:
XHTML:
<h:form id="itemForm">
<p:accordionPanel id="myAccordion" binding="#{accordionView}" value="#{playgroundController.users}" dynamic="true" activeIndex="#{playgroundView.activeIndex}" var="user" multiple="false">
<p:ajax event="tabChange" update="commentDialogID"/>
<p:tab title="#{user.name}">
<h:outputLabel for="itemName" value="Item Name:" />
<p:inputText id="itemName" title="Item Name:" maxlength="16"
value="#{user.amount}">
</p:inputText>
<p:commandButton value="showDialog" onclick="PF('commentDialog').show();"></p:commandButton>
</p:tab>
</p:accordionPanel>
</h:form>
<p:dialog id="commentDialogID" header="Enter comment" widgetVar="commentDialog" modal="true" resizable="true" height="auto">
<h:form id="commentForm">
<h:outputLabel for="comment" value="Comment:"/>
<p:inputTextarea id="comment" title="Comment"
rows="6" cols="33"
value="#{playgroundView.activeIndex}"
required="true">
<f:ajax render="comment"/>
</p:inputTextarea>
<p:commandButton value="Submit" onclick="PF('commentDialog').hide();" update=":itemForm:myAccordion:#{playgroundView.activeIndex}:itemName" ></p:commandButton>
</h:form>
</p:dialog>
PlaygroundView Bean:
Note that in this example the activeIndex has to have an initial value, otherwise this EL:
update=":itemForm:myAccordion:#{playgroundView.activeIndex}:itemName"
will resolve wrongly and will throw an error that it cannot find the id of the referenced component. This of course has the drawback that the first tab will be open when the page loads, but that's another issue
private String activeIndex = "0";
public String getActiveIndex() {
return activeIndex;
}
public void setActiveIndex(String activeIndex) {
this.activeIndex = activeIndex;
}
PlaygroundController Bean:
private List<User> users;
#Override
public void initializeView() {
createUsers();
}
public void createUsers() {
User user1 = new User();
User user2 = new User();
User user3 = new User();
user1.setName("User123");
user1.setAmount(1);
user2.setName("User456");
user2.setAmount(2);
user3.setName("User12312345111111111111111111111111111");
user3.setAmount(3);
List<User> userList = new ArrayList<User>();
userList.add(user1);
userList.add(user2);
userList.add(user3);
users = userList;
}
User:
public class User implements Serializable {
String name;
int amount = 0;
}
You should update the commandButton on the tabChange event.
<p:ajax event="tabChange" listener="#{managedBean.processTabChange}" update=":commentForm:commandButtonId"/>
The render attribute <f:ajax> inside the <h:commandButton>is evaluated during the render phase and not sooner afaik. So if you do not update it, it will always point to '0'. Check the source in your browser developer tool and check your ajax requests (things you should always do for debugging). You will see the full clientId with the ':0:' and you will not see it changing on the ajax requests.
When I enter valid value (only digits) to inputText and click commandButton then I'am redirected to response.xhtml.
When I enter invalid value, click on page background, then change event is triggered, message is displayed.
Now when I enter valid value and click commandButton, message hides, but I am not redirected.
When I click second time I am redirected.
index.xhtml
<?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:h="http://xmlns.jcp.org/jsf/html"
xmlns:f="http://xmlns.jcp.org/jsf/core">
<h:head>
<title>Facelet Title</title>
</h:head>
<h:body>
<h:form>
<h:panelGrid>
<h:inputText id="value" value="#{bean.value}">
<f:validateRegex pattern="\d+"/>
<f:ajax event="change" execute="#this" render="#this valueMessage"/>
</h:inputText>
<h:message id="valueMessage" for="value"/>
</h:panelGrid>
<h:commandButton value="OK" action="response"/>
</h:form>
</h:body>
</html>
response.xhtml
<?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:h="http://xmlns.jcp.org/jsf/html">
<h:head>
<title>Facelet Title</title>
</h:head>
<h:body>
Value is <h:outputText value="#{bean.value}"/>
</h:body>
</html>
Bean.java
package app;
import javax.faces.bean.ManagedBean;
import javax.faces.bean.RequestScoped;
#ManagedBean
#RequestScoped
public class Bean {
private String value;
public String getValue() {
return value;
}
public void setValue(String value) {
this.value = value;
}
}
Your concrete problem is caused by the hiding message, which in turn causes the command button to move up before its own click event finishes, and thus not sitting below the mouse pointer anymore when the mouse button is released and about to trigger the form submit. If you move the message component to below the command button component like so,
<h:commandButton value="OK" action="response"/>
<h:message id="valueMessage" for="value"/>
... then the command button will stay at the same place in the UI when the message hides, and thus its click event will be successfully received and trigger the form submit.
This is just an UI design problem, not a technical problem. You need to try to make sure that the button doesn't move away before its click event finishes.
One way is to make the form 2-column wide so that the message is shown next to the input field instead of below it, hereby not occupying more horizontal space:
<h:panelGrid columns="2">
An alternative is to delay the ajax request with <f:ajax delay> attribute (only available since JSF 2.2). I tested with a few values and 100~200ms seems acceptable to cover the time span of an average click (the time from the mouse button press until the mouse button is actually released). This way the ajax request is actually not fired when the submit button is clicked, hereby avoiding the shift of the button (and "unnecessary" validation).
<f:ajax render="#this valueMessage" delay="150" />
(note that event="change" and execute="#this" are the default values already, they are already for clarity omitted in above example; technically render="#this" is also strange, but I just kept it in, for the case you actually want to redisplay a changed value after some conversion step)
You may have to do it like this in your index.xhtml
<h:form>
<h:panelGrid>
<h:inputText id="value" value="#{bean.value}">
<f:validateRegex pattern="\d+" />
</h:inputText>
<h:message id="valueMessage" for="value"/>
</h:panelGrid>
<h:commandButton value="OK" action="response" >
<f:ajax execute="value" render="value valueMessage"/>
</h:commandButton>
</h:form>
and btw in your code the first click is for change event not for the button so if you try to click anywhere in the page except the button it will make the validation to disappear and the button will redirect you from first hit.
I have a jsf/primefaces page, with a tabView, that does not work as I would expect it to.
If I make a selection on tab 1, then move to tab 2 and save, it works fine. If I stay on tab 2 and save a second time, it displays the required message for the required field on the first tab.
If I return to the first tab, it is filled in, as it should be. If I press Save again now, on the first tab, it works again.
Why does jsf/primefaces think this field is not filled in?
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:ui="http://java.sun.com/jsf/facelets"
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:body>
<div style="height: 50px;">
<p:messages id="messages" autoUpdate="true" showDetail="true" closable="true" />
</div>
<h:form id="form">
<p:tabView id="tabs" dynamic="true">
<p:tab id="tab1" title="Tab 1">
<h:outputLabel value="Field 1" />
<h:selectOneMenu required="true" requiredMessage="Must fill in field 1 selection">
<f:selectItem itemLabel="Select..." itemValue="" noSelectionOption="true" />
<f:selectItem itemLabel="1" itemValue="1" />
</h:selectOneMenu>
<br/>
</p:tab>
<p:tab id="tab2" title="Tab 2">
<h:outputLabel value="Field 2" />
<p:inputText required="false" />
<br/>
</p:tab>
</p:tabView>
<p:commandButton value="Save" ajax="true" action="#{tabBean.action}" update="form"
oncomplete="setTimeout(function(){$('[id$=messages]').fadeOut()},'500')" />
</h:form>
</h:body>
</html>
Bean:
#ViewScoped
#ManagedBean(name = "tabBean")
public class TabBean implements Serializable {
private static final long serialVersionUID = 1L;
public TabBean() { }
#PostConstruct
public void init(){ }
public void action() {
// do the save, if validation doesn't fail first
addJsfMessage(FacesMessage.SEVERITY_INFO, "OK", "no prob");
}
private void addJsfMessage(Severity severity, String summary, String detail) {
FacesMessage msg = new FacesMessage(severity, summary, detail);
FacesContext.getCurrentInstance().addMessage(null, msg);
}
}
While creating this sample, I found that either of the following changes got rid of the error, but neither is desirable:
turning off dynamic loading (the primefaces documentation says in dynamic mode, only the active tab contents
are rendered and when an inactive tab header is selected, content is loaded with ajax, which I assumed to mean on the first load but it seems to not just hide already-loaded tabs, but not render them at all)
replacing the <h:selectOneMenu ... /> with: <h:inputText required="true" requiredMessage="Must fill in field 1 text" />
It's caused by the combination of <p:tabView dynamic="true"> and <p:commandButton update="form">.
When (re)rendered, a dynamic tabview only contains the currently active tab, not others. Only when you change the tab, then it will be loaded into the HTML DOM tree asynchronously. Now, you're on submit updating the entire form, including the dynamic tabview, so it re-renders with only the currently active tab reloaded. In your particular case, the first tab doesn't exist in the HTML DOM tree at all and therefore nothing can be sent to the server side.
Try being more specific in <p:commandButton update>. Specify only the components which really need to be updated, not the entire form with the dynamic tabview.
i've a problem that's driving me crazy! I have a component which renders a panel that have links on a footer. This links have, among other things, a title and HTML content I want to show. The idea is basically that when a link is clicked, the content of the page is changed via ajax and the html content of the link is shown (The footer remains on the page). Debbuging I've noticed that the first time a link is clicked the content does change, but then it doesn't. The setPropertyActionListener of the p.commandlinks in my composite component aren't executed any more after first time. I'have debbuged the project and this seems to be the problem, but I can't figure out why
Part of my composite component. Tryed setting an onstart and oncomplete js functions on the command link, and both are executed but the actionListeners aren't.
<cc:interface>
<cc:attribute name="compId"/>
<cc:attribute name="title"/>
<cc:attribute name="bean"/>
...
</cc:interface>
<!-- IMPLEMENTATION -->
<cc:implementation>
<p:panel id="#{cc.attrs.compId}" header="#{cc.attrs.title}"
widgetVar="#{cc.attrs.compId}"
styleClass="#{cc.attrs.styleClass}">
<h:form>
<p:dataList id="datalist-#{cc.attrs.compId}" value="#{cc.attrs.bean.currentList}"
type="definition" widgetVar="datalist-#{cc.attrs.compId}"
var="items" >
<p:commandLink action="#{navBean.active}" update="#([id$=content])" styleClass="link"
value="#{items.descrInLang}" rendered="#{items.typeUrl eq 0}">
<f:setPropertyActionListener value="infoPage" target="#{navBean.selection}"/>
<f:setPropertyActionListener value="#{items.descrInLang}" target="#{infoPage.title}" />
<f:setPropertyActionListener value="#{items.HTMLContent}" target="#{infoPage.HTMLContent}"/>
</p:commandLink>
</p:dataList>
</h:form>
</p:panel>
</cc:implementation>
Part of my Navigation Bean
public void active() throws IllegalArgumentException, IllegalAccessException, NoSuchFieldException {
setAlbums(selection.equals("albums"));
setBand(selection.equals("band"));
setConcerts(selection.equals("concerts"));
setContacts(selection.equals("contacts"));
setHome(selection.equals("home"));
setSiteMap(selection.equals("siteMap"));
setInfoPage(selection.equals("infoPage"));
}
public void setSelection(String selection) {
this.selection = selection;
}
public boolean isInfoPage() {
return infoPage;
}
public void setInfoPage(boolean infoPage) {
this.infoPage = infoPage;
}
The bean which stores the title & HTML Content to show. After the first click on the link, the getters of Title & HTML content are executed, but the setters aren't (as declared in the setPropertyActionListeners)
#ManagedBean
#SessionScoped
public class InfoPage implements Serializable {
private String title;
private String HTMLContent;
public InfoPage() {
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getHTMLContent() {
return HTMLContent;
}
public void setHTMLContent(String HTMLContent) {
this.HTMLContent = HTMLContent;
}
}
The content part of the template which indicates what content should be shown according to the selected page on the NavBean. I pass the HTML content and the title of the infoPage as a parameter
<h:head>
</h:head>
<h:body>
<!-- the all containing file -->
<ui:composition template="template.xhtml">
<ui:define name="content">
<h:panelGroup layout="block" id="content">
<h:panelGroup layout="block" rendered="#{navBean.home}">
<ui:include src="homecontent.xhtml"/>
</h:panelGroup>
....
<h:panelGroup layout="block" rendered="#{navBean.infoPage}">
<ui:include src="infoPage.xhtml">
<ui:param name="title" value="#{infoPage.title}"/>
<ui:param name="infoContent" value="#{infoPage.HTMLContent}"/>
</ui:include>
</h:panelGroup>
</h:panelGroup>
</ui:define>
</ui:composition>
</h:body>
The info Page.
<h:form id="infoPageForm">
<p:panel id="infoPagePanel" header="#{title}">
<h:panelGrid columns="1" cellpadding="10">
<h:outputText escape="false" value="#{infoContent}" />
</h:panelGrid>
</p:panel>
</h:form>
Thanks for your time!
I'm trying to code a JSF page with a pop-up dialog that allows users to enter data. I've used PrimeFaces before, but for various reasons I've decided not to use it for this project. After all, I just need a simple pop-up dialog. How hard could it be to code it myself?
The ways it's suppose to work is:
The user clicks the Add New Record button and the dialog is shown.
The user enters data and clicks the Save button.
If there are validation errors, the messages are displayed in the dialog and the dialog remains visible.
If there are no errors, a success message is shown on the main page and the dialog disappears.
The user can press the Cancel button to close the dialog without saving.
My problem is that when the user presses Save and gets validation errors and then presses Cancel, the dialog does not disappear. I can see that under this scenario, the value for the inputHidden field does not get updated to false. The isShowDialog() method on the backing bean does not get called.
Here is my facelet:
<?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:f="http://java.sun.com/jsf/core"
xmlns:h="http://java.sun.com/jsf/html">
<h:head>
<h:outputScript library="js" name="jquery-1.11.0.js" />
</h:head>
<h:body>
<h:messages id="pageMessages" errorStyle="color: red;" infoStyle="color: green;" />
<script type="text/javascript">
function showHideDialog(data) {
if (data.status == "success") {
if ($("#dialogForm\\:showDialog").val() == "true") {
$("#dialogDiv").show();
$("#pageMessages").hide();
} else {
$("#dialogDiv").hide();
$("#pageMessages").show();
}
}
}
</script>
<div id="dialogDiv" style="border: thick solid black; display: none;">
<h:form id="dialogForm">
<h:messages id="dialogMessages" errorStyle="color: red;" infoStyle="color: green;" />
<h:inputHidden id="showDialog" value="#{backingBean.showDialog}" />
<h:panelGrid columns="2">
<h:outputLabel for="dialogField" value="Some Field:" />
<h:inputText id="dialogField" value="#{backingBean.dialogField}" label="Some field" required="true" />
</h:panelGrid>
<h:commandButton value="Save" action="#{backingBean.save}">
<f:ajax event="action" execute=":dialogForm" render=":dialogForm :pageMessages" onevent="showHideDialog" />
</h:commandButton>
<h:commandButton value="Cancel" action="#{backingBean.cancel}" immediate="true">
<f:ajax event="action" render=":dialogForm :pageMessages" onevent="showHideDialog" />
</h:commandButton>
</h:form>
</div>
<h:form id="pageForm">
<p>Normal page content. Blah, blah, blah.</p>
<h:commandButton value="Add New Record" action="#{backingBean.addNew}">
<f:ajax event="action" render=":dialogForm :pageMessages" onevent="showHideDialog" />
</h:commandButton>
</h:form>
</h:body>
</html>
Here is my backing bean:
package com.mycompany.example;
import javax.faces.application.FacesMessage;
import javax.faces.bean.ManagedBean;
import javax.faces.bean.ViewScoped;
import javax.faces.context.FacesContext;
#ManagedBean
#ViewScoped
public class BackingBean {
private String dialogField;
private boolean showDialog = false;
public String addNew() {
dialogField = null;
showDialog = true;
return null;
}
public String save() {
FacesContext.getCurrentInstance().addMessage(null, new FacesMessage(FacesMessage.SEVERITY_INFO, "New row successfully saved.", null));
showDialog = false;
return null;
}
public String cancel() {
showDialog = false;
return null;
}
public String getDialogField() { return dialogField; }
public void setDialogField(String dialogField) { this.dialogField = dialogField; }
public boolean isShowDialog() { return showDialog; }
public void setShowDialog(boolean showDialog) { this.showDialog = showDialog; }
}
If it matters, I'm using Mojarra 2.2.5 and Tomcat 7.0.42.
Any suggestions or insights would be appreciated.
I found an answer. I changed
<h:inputHidden id="showDialog" value="#{backingBean.showDialog}" />
to
<input id="dialogForm:showDialog" type="hidden" value="#{backingBean.showDialog}" />
I found the answer reading the following BalusC article: Okay, when should I use the immediate attribute?
In short, use
immediate="true" in <h:inputHidden id="showDialog" too,
and
execute="showDialog" in <f:ajax inside <h:commandButton value="Cancel".
P.S.: Your example code was very useful.