论坛首页 Java版

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

浏览 774 次
精华帖 (0) :: 良好帖 (0) :: 新手帖 (0) :: 隐藏帖 (0)
作者 正文
最后更新时间:2007-08-28 关键字: JSF
我的系统里采用了左树右表的形式来展示我们的用户界面,点击左树的节点,右边会根据点击的节点生成相应表的数据,使用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的代码了啊?
   
最后更新时间:2007-08-29
我想这是生命周期的问题:
在  class WelcomePage 里面
        DynPage dynPage = new DynPage(); 
        dynPage.init(); 
dynPage 是构造了再执行init()的。
当你从welcome.jsp跳到dynpage.jsp,这是正常的。
但是当你再次提交dynpage.jsp的时候,jsf会先恢复视图,然后才执行
  public void execute(ActionEvent event) { 
         init(); 
     }
可能在恢复视图的时候自动的执行了一次init(),所以必须加上上面那句话,否则会导致重复创建组件而出错。
我想除了这么想,好像也没其他的可能了。
   
0 请登录后投票
最后更新时间:2007-08-30
我想也可能是生命周期的问题。
但我想更具体的了解JSF一些内在的机制。
我看了JSF生成的HTML页面,页面好像有个隐藏域,id是com.sun.faces.VIEW,好像是JSF将视图中的所有UI都序列化到了这里,当提交form时,JSF生命周期的第一个阶段是恢复试图,这时它是不是就通过反序列化这个id为com.sun.faces.VIEW的隐藏域中的内容来恢复各个组间的呢。
有无高手讲解下这个内在的处理?

另外还有个问题:
我曾试过将一个inputText的disabled属性通过值binding的形式赋了值,如下:
<h:inputText id="next" disabled="#{!dynPage.hasNext}" value=#{dynPage.name}>
结果当我在页面上通过JS将其激活,并提交form时,dynPage的name属性没有被更新为最新的值。
我自己试了几次,好像是只要在JSF渲染响应时UI的disabled属性为true,那么再提交form,那么这些disabled属性为true的UI就不会用最新的值进行更新了,即使你在页面上通过JS将UI激活。
而当commandButton的disabled为true,然后你在页面上用JS将其激活,这时点击这个按钮,那么JSF并不会执行你指定的action、actionListener,而只是简单的重新刷新本页面而已。

JSF中有那么多奇怪的东西,是否有哪个可以帮我们大概讲下其中的一些原理啊?
   
0 请登录后投票
论坛首页 Java版

跳转论坛:
JavaEye推荐