amphibient
amphibient

Reputation: 31212

How to AJAX update components in single tab of p:accordionPanel from outside?

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

Answers (2)

Emil Kaminski
Emil Kaminski

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

Kukeltje
Kukeltje

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

Related Questions