单击 CommandButton 在页面上动态实例化复合组件

Dynamically instantiate composite components on page, on clicking CommandButton

继传说中的 BalusC 对此 post 的回应之后:

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

如果我有足够的分数,我会附上评论 post -- 但我没有足够的分数。

问题如下

我正在尝试设置一个 commandButton,它将 JSF 复合组件动态添加到 xhtml 文件。目的是多次单击它会将组件的多个实例放在页面上。

所以我在 XHTML 文件上有一个按钮:

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

调用 AssessingController 的方法:

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

UIComponent parent = null; —— 因为在传递到 includeCompositeComponent 之前必须以某种方式实例化或引用它。但如下所述 - 将其设置为 null 可能会导致空指针异常,(那么我应该怎么做呢?)

includeCompositeComponent 方法按照 BalusC 在上面 post:

中引用的 JSF 2.2 方法
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);
}

当我点击 commandButton 时,记录如下:

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 是这一行:

parent.getChildren().add(composite);

复合不为空(已检查)。 所以,很明显,也许 - parent 为空,这就是问题所在。

那么我怎样才能更好地参考 UIComponent parent 开始呢? 我需要让它引用 xhtml 文件上的内容吗?我假设它需要某种占位符来充当 parent(?)。现在 xhtml 页面上只有 commandButton。

谢谢大家。

parent 应该表示您要在其中包含复合组件的组件。

想象一下,您最终想要以这种简单的 XHTML 表示形式结束:

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

然后您应该准确地提供 <h:panelGroup> 组件作为 parent

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

或者,通过传递具体组件本身:

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

与具体问题无关:这里有一个 thinking/design 错误。您应该使用 <ui:repeat><your:compositeComponent>,然后将 @ViewScoped bean 提供给 <ui:repeat> 表示复合模型值的动态大小的实体列表。

<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());
}

任何时候您需要处理支持 bean 中的原始 UIComponent 实例,暂停一下并仔细研究或询问您是否真的以正确的方式做事。也许它属于支持组件,或者可以只用纯 XHTML 完成。

另请参阅:

  • How to dynamically add JSF components
  • How does the 'binding' attribute work in JSF? When and how should it be used?