Reputation: 31212
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
?
Upvotes: 3
Views: 3202
Reputation: 1885
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;
}
Upvotes: 1
Reputation: 12337
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.
Upvotes: 0