将新组件动态添加到 JSF 组件树时重复 id

Posted

技术标签:

【中文标题】将新组件动态添加到 JSF 组件树时重复 id【英文标题】:Duplicate id when dynamically adding new component to JSF component tree 【发布时间】:2012-11-21 02:25:47 【问题描述】:

使用 JSF 2.1、Mojarra 2.1.3、Glassfish 3.1.1 和 PrimeFaces 3.3.1

我正在尝试在 JSF 中处理 preRenderView 事件中的字段级安全性,并且在需要将动态组件添加到 JSF 组件树时遇到问题。在第一次渲染时,一切都很好,并且处理了现场级别的安全性。然而,在任何类型的更新之后,Mojarra 抱怨重复的 id,即使通过打印到控制台,我的添加代码每次更新只运行一次。

看起来好像 Mojarra 没有为回发清除组件树,因此每个后续渲染更新都会向树中添加组件的附加版本。

感谢任何人提供的任何帮助。这是一些简化的示例代码。在 commandButton 单击时会引发错误。

index.xhtml:

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:h="http://java.sun.com/jsf/html"
      xmlns:f="http://java.sun.com/jsf/core"
      xmlns:p="http://primefaces.org/ui"
        >
    <f:event listener="#lifeCycle.event" type="preRenderView" />
    <h:body>
        <h:form id="form" prependId="false">
            <h:panelGroup id="testPanel">
                <h:inputText id="viewSec" value="viewSec node"/><br/>
            </h:panelGroup>
            <p:commandButton update="testPanel"/>
        </h:form>
    </h:body>
</html>

LifeCycle.java:

package com.dynamic.test;

import java.io.Serializable;
import java.util.List;
import javax.faces.bean.ManagedBean;
import javax.faces.bean.RequestScoped;
import javax.faces.component.UIComponent;
import javax.faces.component.UIViewRoot;
import javax.faces.component.html.HtmlOutputText;
import javax.faces.component.visit.VisitContext;
import javax.faces.context.FacesContext;
import javax.faces.event.ComponentSystemEvent;

@ManagedBean
@RequestScoped
public class LifeCycle implements Serializable 
    public void event(ComponentSystemEvent event)
        FacesContext facesContext = FacesContext.getCurrentInstance();
        UIViewRoot root = facesContext.getViewRoot();
        NodeInspector visitCallback = new NodeInspector();

        root.visitTree(VisitContext.createVisitContext(FacesContext.getCurrentInstance()), visitCallback);

        List<UIComponent> securityEnabledComponents = visitCallback.getSecurityEnabledComponents();
        for (UIComponent securityEnabledComponent : securityEnabledComponents) 

            if(securityEnabledComponent.getClientId().equals("viewSec"))
                List<UIComponent> childList = securityEnabledComponent.getParent().getChildren();
                int targetPosition = securityEnabledComponent.getParent().getChildren().indexOf(securityEnabledComponent);

                HtmlOutputText outputTextComponent = new HtmlOutputText();
                outputTextComponent.setId(securityEnabledComponent.getId());
                outputTextComponent.setValue(securityEnabledComponent.getAttributes().get("value"));

                childList.set(targetPosition, outputTextComponent);
            
        
    

NodeInspector.java

package com.dynamic.test;

import java.util.ArrayList;
import java.util.List;
import javax.faces.component.UIComponent;
import javax.faces.component.visit.VisitCallback;
import javax.faces.component.visit.VisitContext;
import javax.faces.component.visit.VisitResult;
import javax.faces.context.FacesContext;

public class NodeInspector implements VisitCallback 
    private List<UIComponent> securityEnabledComponents = new ArrayList<UIComponent>();
    FacesContext facesContext = FacesContext.getCurrentInstance();

    @Override
    public VisitResult visit(final VisitContext context, final UIComponent target) 
        if(target.getClientId().equals("viewSec"))
            securityEnabledComponents.add(target);
        
        return VisitResult.ACCEPT;
    

    public List<UIComponent> getSecurityEnabledComponents() 
        return securityEnabledComponents;
    

错误:

SEVERE: JSF1007: Duplicate component ID viewSec found in view.
    ...  +id: j_idt2
       type: <html xmlns="http://www.w3.org/1999/xhtml">

      +id: j_idt3
       type: javax.faces.component.UIOutput@17098e7
        +id: form
         type: javax.faces.component.html.HtmlForm@14677de
          +id: testPanel
           type: javax.faces.component.html.HtmlPanelGroup@167ab25
            +id: viewSec  <===============
             type: javax.faces.component.html.HtmlOutputText@14a3d2e
            +id: viewSec  <===============
             type: javax.faces.component.html.HtmlOutputText@f6a607
            +id: j_idt4
             type: <br/>

          +id: j_idt5
           type: org.primefaces.component.commandbutton.CommandButton@1380fc5
      +id: j_idt6
       type: 
    </html>...

    SEVERE: Error Rendering View[/index.xhtml]
    java.lang.IllegalStateException: Component ID viewSec has already been found in the view.  
        at com.sun.faces.util.Util.checkIdUniqueness(Util.java:821)...

【问题讨论】:

今天早上我才弄清楚是什么导致了我的问题 - 我运行的 Mojarra 版本有很多错误,与动态修改树有关,这些错误大多已被最近解决版本(see bug report here)。在修复错误的过程中,他们似乎遇到了与我相同的问题。修改树会导致大量孤立节点闲置。这些节点实际上并不在呈现的 HTML 中,但仍然检查了 id 唯一性。 您应该发布此评论作为答案。了解一下很有用。 【参考方案1】:

检查您使用的 Mojarra 版本是否与 Primefaces 3.3.1 兼容。 使用最新版本的 Primefaces 3.6。

【讨论】:

以上是关于将新组件动态添加到 JSF 组件树时重复 id的主要内容,如果未能解决你的问题,请参考以下文章

JSF中的重复组件ID [重复]

如何在 Ajax 请求期间在 JSF2 中动态添加组件

JSF 2.0 动态删除组件

在运行时添加您在编译时不知道的 JSF 组件 [重复]

如何动态添加输入字段[重复]

如何在 JSF 中动态添加字段?