[Ajax4JSF] 【请教】JSF中动态生成表格时为何出现重复的组件ID的错误

Hotpepper 2007-08-26
我的系统里采用了左树右表的形式来展示我们的用户界面,点击左树的节点,右边会根据点击的节点生成相应表的数据,使用JSF实现右边的动态生成时(使用同一份代码来满足所有的需求),当在右表页面中点击分页按钮,重新显示右表页面时确出现了如下类似的问题:“在视图中找到了重复的组件 ID "dynform:_id0:_id4"。”。
以下用类似代码来说明这个问题:
1、使用一个页面来模拟我们的左树的点击功能,这个中只有一个按钮,点击它后会生成右边表格显示所需的Bean。
JSP页面(welcome.jsp):
<%@ taglib uri="http://java.sun.com/jsf/core" prefix="f" %>
<%@ taglib uri="http://java.sun.com/jsf/html" prefix="h" %>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">

<f:view>
<body>
<h:form id="welcomeform">		
<h:commandButton value="jump" action="#{welcomeForm.execute}"/>
</h:form>
</body>
</f:view>



JAVA代码:
package com.savage.dynweb;

import javax.faces.context.FacesContext;
import javax.faces.el.ValueBinding;

public class WelcomePage {
	public String execute() {
		DynPage dynPage = new DynPage();
		dynPage.init();
		FacesContext context = FacesContext.getCurrentInstance();
		ValueBinding vb = context.getApplication().createValueBinding("#{requestScope.dynPage}");
		vb.setValue(context, dynPage);
		return "success";
	}
}


faces-config.xml:
	<managed-bean>
		<managed-bean-name>welcomeForm</managed-bean-name>
		<managed-bean-class>com.savage.dynweb.WelcomePage</managed-bean-class>
		<managed-bean-scope>request</managed-bean-scope>
	</managed-bean>	
	
	<navigation-rule>
		<from-view-id>/pages/test.jsp</from-view-id>
		<navigation-case>
			<from-outcome>success</from-outcome>
			<to-view-id>/pages/dynpage.jsp</to-view-id>
		</navigation-case>
	</navigation-rule>


右表页面展示表格中的数据,还有分页按钮,点击分页按钮,使用如下代码模拟:
JSP页面(dynpage.jsp):
<%@ taglib uri="http://java.sun.com/jsf/core" prefix="f" %>
<%@ taglib uri="http://java.sun.com/jsf/html" prefix="h" %>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">

<f:view>
<body>
<h:form id="dynform">
<h:dataTable binding="#{dynPage.dynTable }" var="row" value="#{dynPage.records }"/>
<h:commandButton value="next" actionListener="#{dynPage.execute}"/>
</h:form>
</body>
</f:view>


JAVA代码:
package com.savage.dynweb;

import java.util.*;

import javax.faces.application.Application;
import javax.faces.component.*;
import javax.faces.context.FacesContext;
import javax.faces.el.ValueBinding;
import javax.faces.event.ActionEvent;

public class DynPage {
	private UIData dynTable;
	
	private List<Map<String, String>> records;
	
	public void init() {
		String[] headers = new String[] {"name", "age", "sex", "birthday"};
		
		//由于不想在构造方法做过多的业务,我单独写了个初始化方法,在页面展示前必定先调用这个方法
		//虚拟生成数据的代码
		records = new ArrayList<Map<String, String>>(10);
		for(int i = 0; i < 10; i++) {
			Map<String, String> record = new HashMap<String, String>(4);
			record.put("name", "name" + i);
			record.put("age", "age" + i);
			record.put("sex", "sex" + i);
			record.put("birthday", "birthday" + i);
			records.add(record);
		}
		
		FacesContext.getCurrentInstance().getViewRoot().getChildren().clear();
		Application app = FacesContext.getCurrentInstance().getApplication();
		
		//以下代码生成页面上要展示的表格内容
		dynTable = new UIData();
		for(String header : headers) {
			UIColumn column = new UIColumn();
			UIOutput output = new UIOutput();
			ValueBinding vb = app.createValueBinding("#{requestScope.row." + header + "}");
			output.setValueBinding("value", vb);
			UIOutput facet = new UIOutput();
			facet.setValue(header);
			column.setHeader(facet);
			column.getChildren().add(output);
			dynTable.getChildren().add(column);
		}
	}
	
	public void execute(ActionEvent event) {
		init();
	}

	public UIData getDynTable() {
		return dynTable;
	}

	public void setDynTable(UIData dynTable) {
		this.dynTable = dynTable;
	}

	public List<Map<String, String>> getRecords() {
		return records;
	}

	public void setRecords(List<Map<String, String>> records) {
		this.records = records;
	}
	
	
}


faces-config.xml:
	<managed-bean>
		<managed-bean-name>dynPage</managed-bean-name>
		<managed-bean-class>com.savage.dynweb.DynPage</managed-bean-class>
		<managed-bean-scope>request</managed-bean-scope>
	</managed-bean>	


就上面的代码,在我从welcome.jsp点击按钮进入dynpage.jsp时,没有任何问题,一切都和我期望的一样,但当我在dynpage.jsp页面中点击了next按钮调用了execute方法后,重新进入dynpage.jsp页面时,确出现了如下异常:
java.lang.IllegalStateException: 在视图中找到了重复的组件 ID "dynform:_id0:_id4"。
com.sun.faces.application.StateManagerImpl.checkIdUniqueness(StateManagerImpl.java:201)
com.sun.faces.application.StateManagerImpl.checkIdUniqueness(StateManagerImpl.java:204)
com.sun.faces.application.StateManagerImpl.checkIdUniqueness(StateManagerImpl.java:204)
com.sun.faces.application.StateManagerImpl.saveSerializedView(StateManagerImpl.java:97)
com.sun.faces.taglib.jsf_core.ViewTag.doAfterBody(ViewTag.java:189)
org.apache.jsp.pages.dynpage_jsp._jspx_meth_f_005fview_005f0(dynpage_jsp.java:104)
org.apache.jsp.pages.dynpage_jsp._jspService(dynpage_jsp.java:67)


我自己试了些方法,最终以在DynPage的init()方法中加了如下一个代码解决了问题:
FacesContext.getCurrentInstance().getViewRoot().getChildren().clear();

虽然问题解决了,看了一些书,但对于为何会有这种结果,还是没搞太懂,请们解答下,非常感谢!

我非常喜欢JSF,在开发中发现JSF有些基本的东西必须搞懂,否则很难掌握这门技术,我感觉这个应该和JSF的生命周期有关,而且在我的开发中确实因为开始没有弄清楚JSF的生命周期的神奇作用而吃了很多亏,上次我在这里发了个贴,希望们指点下JSF的生命周期,却没有人回帖,可能是没有把问题具体化而导致,今天我把我遇到的一个问题贴出来,希望大家可以探讨下,我相信很多初学者应该会遇到我类似的问题,希望通过这个帖子,可以使大家少走些冤枉路^_^
Hotpepper 2007-08-27
晕倒!发了贴都没有人回的,难道都没人会?????

相当的失望啊!!!!
john.yi 2007-09-01
的组件 ID "dynform:_id0:_id4" 这句貌似自动生成的FORM ID
gflei 2008-12-31
也遇到类似问题,我们的现象是偶尔发生,不是每次发生。
Global site tag (gtag.js) - Google Analytics