1
votes

I am trying to display HtmlInputText dynamically in a JSF page. However, I am getting

javax.faces.FacesException: Cannot add the same component twice: j_idt10:hitDyn

During the first request to the page the input text renders well. That exception happens during postback of the page, when I enter some text in the input component and press Enter. In the .xhtml page, I have the following code:

<h:form>
    <h:outputLabel value="Welcome!"></h:outputLabel>
    <f:metadata>
        <f:event type="preRenderView" listener="#{dynamicBacking.addDynComp}" />
    </f:metadata>
    <h:panelGroup id="dynOuter"></h:panelGroup>
</h:form>

In the backing bean, I have the following code:

@ManagedBean(name="dynamicBacking")
public class DynamicBacking {
    public void addDynComp() {
        Application app = FacesContext.getCurrentInstance().getApplication();
        HtmlInputText hit = (HtmlInputText)app.createComponent(HtmlInputText.COMPONENT_TYPE);
        hit.setId("hitDyn");
        UIComponent parent = findComponent("dynOuter");
        if( parent != null ) {
            parent.getChildren().add(hit);
        }
    }

    public UIComponent findComponent(final String id) {
        FacesContext context = FacesContext.getCurrentInstance(); 
        UIViewRoot root = context.getViewRoot();
        final UIComponent[] found = new UIComponent[1];
        root.visitTree(new FullVisitContext(context), new VisitCallback() {     
            @Override
            public VisitResult visit(VisitContext context, UIComponent component) {
                if(component.getId().equals(id)){
                    found[0] = component;
                    return VisitResult.COMPLETE;
                }
                return VisitResult.ACCEPT;              
            }
        });
        return found[0];
    }
}

I guess that there is some problem with restoring the state of the dynamic component in a postback. Am I adding the dynamic component too late in the lifecycle of the JSF page? I know that in ASP.NET I could add a dynamic control during Page.Load phase. But I can't so far figure out how to achieve the same in JSF. Please, help!

1
I guess that the listener method gets called on postback and it tries to add the same component again but it already exists and you get an exception for that. - Adrian Mitev
How does it happen that the component already exists when the listener method is called on postback? Who created the component during postback? - Serge Rogatch
The component is added in the tree on the initial page load. When performing a postback your listener gets called again and it tries to add another component with the same id. - Adrian Mitev
Doesn't the tree get destructed after the initial page is served? I thought that JSF builds a new component tree upon each request, including a postback. If not, does JSF store the component tree somewhere so that the tree doesn't need to be reconstructed on postback? - Serge Rogatch
Then this answers the question because it explains why the component exists on postback. One needs to add the following to the beginning of addDynComp method: if( FacesContext.getCurrentInstance().isPostback() ) { return; } - Serge Rogatch

1 Answers

0
votes

The exception appears because the component is added in the tree on the initial page load. When performing a postback your listener gets called again and it tries to add another component with the same id and this causes the exception. A solution of the issue is to check if the request is NOT a postback when adding the component. The following code shows how to check for postback:

if (FacesContext.getCurrentInstance().isPostback()) {....