1

Following on from the response by the legendary BalusC to this post:

How to programmatically or dynamically create a composite component in JSF 2

Had I sufficient points I would attach a comment to that post -- but I don't have sufficient points .

Problem as follows.

I'm trying to set up a commandButton which adds a JSF composite component dynamically to an xhtml file. With the intention that clicking it multiple times will put multiple instances of the component on the page.

So I have a button on an XHTML file:

<h:commandButton action="#{assessingController.addQuestion}" value="Add a   Question"></h:commandButton>

Which calls a method on AssessingController:

public void addQuestion() {
    UIComponent parent = null;
    includeCompositeComponent(parent, "http://xmlns.jcp.org/jsf/composite/components", "questionComposite", "someId");   
}

UIComponent parent = null; -- because it has to be instantiated or referenced somehow, before being passed into includeCompositeComponent . But as noted below - making it null might be causing the null pointer exception, (so what should I do instead?)

includeCompositeComponent method is as per the JSF 2.2 method referred to by BalusC in the above post:

public void includeCompositeComponent(UIComponent parent, String taglibURI, String tagName, String id) {
    FacesContext context = FacesContext.getCurrentInstance();
    UIComponent composite = context.getApplication().getViewHandler()
        .getViewDeclarationLanguage(context, context.getViewRoot().getViewId())
        .createComponent(context, taglibURI, tagName, null);
composite.setId(id);
    parent.getChildren().add(composite);
}

When I click on the commandButton, logs as follows:

javax.faces.el.EvaluationException: java.lang.NullPointerException
at javax.faces.component.MethodBindingMethodExpressionAdapter.invoke(MethodBindingMethodExpressionAdapter.java:101)
at com.sun.faces.application.ActionListenerImpl.processAction(ActionListenerImpl.java:102)
at javax.faces.component.UICommand.broadcast(UICommand.java:315) ...
Caused by: java.lang.NullPointerException
at controllers.AssessingController.includeCompositeComponent(AssessingController.java:123)

AssessingController.java:123 is this line:

parent.getChildren().add(composite);

Composite is not null (checked that). So, obviously perhaps, - parent is null and that's where the problem is.

So how can I better reference UIComponent parent to begin with? Do I need to make it refer to something on the xhtml file? I'm presuming it needs some kind of a placeholder which will serve as the parent(?). Right now all there is on the xhtml page is the commandButton.

Thank you all.

Community
  • 1
  • 1
anvw
  • 149
  • 3
  • 15

1 Answers1

1

The parent is supposed to represent the component where you'd like to include the composite component in.

Imagine that you ultimately want to end up with this plain XHTML representation:

<h:panelGroup id="questions">
    <your:questionComposite />
</h:panelGroup>

You should then supply exactly that <h:panelGroup> component as parent.

<h:form> 
    <h:commandButton ... action="#{bean.addQuestion}" />
</h:form>
<h:panelGroup id="questions" />
public void addQuestion() {
    UIComponent parent = context.getViewRoot().findComponent("questions");
    // ...
}

Or, by passing the concrete component itself:

<h:form> 
    <h:commandButton ... action="#{bean.addQuestion(questions)}" />
</h:form>
<h:panelGroup id="questions" binding="#{questions}" />
public void addQuestion(UIComponent parent) {
    // ...
}

Unrelated to the concrete problem: there's a thinking/design mistake here. You should rather use an <ui:repeat><your:compositeComponent> and then feed from a @ViewScoped bean to the <ui:repeat> a dynamically sized list of entities representing the composite's model value.

<h:form> 
    <h:commandButton ... action="#{bean.addQuestion}" />
</h:form>
<ui:repeat value="#{bean.questions}" var="question">
    <your:questionComposite value="#{question}" />
</ui:repeat>
private List<Question> questions;

public void addQuestion() {
    questions.add(new Question());
}

Anytime you need to deal with raw UIComponent instances in a backing bean, take a pause and carefully research or ask if you're really doing things the right way. Perhaps it belongs in a backing component instead, or could just be done with pure XHTML.

See also:

Community
  • 1
  • 1
BalusC
  • 1,082,665
  • 372
  • 3,610
  • 3,555
  • Thank you BalusC -- very much appreciated. It will be a bit of time before I can delve into this, but the thoroughness of your response is, as ever, really helpful. Thanks to Tiny for the remarks on style. Merry Christmas to you all. – anvw Dec 23 '15 at 22:17
  • As a footnote, ultimately this solution seemed to work for the context - http://stackoverflow.com/questions/3409053/how-to-dynamically-add-jsf-components .. In other words - composite component not needed. – anvw Jan 10 '16 at 06:24
  • There are indeed no cases where a composite component is technically the only solution. – BalusC Jan 10 '16 at 08:00