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()) {....