|
锁定老贴子 主题:深入理解Tapestry的Rewind
精华帖 (0) :: 良好帖 (0) :: 新手帖 (0) :: 隐藏帖 (0)
|
|
|---|---|
| 作者 | 正文 |
|
时间:2006-12-30 关键字: tapestry rewind
Tapestry的rewind一直是学习和使用Tapestry的难点,rewind是用来处理表单提交的,表单默认使用的是DirectService来提交。在详细介绍之前,先说明下此文中需要用到的一些概念,首先是表单组件,我这里指的是指继承自AbstractFormComponent类的组件,例如:TextField、TextArea、Checkbox等,而不是具体的Form组件,表单组件使用时必须在Form组件中,这些组件在rewind时调用继承自AbstractFormComponent的rewindFormComponent来读取数据,并将数据赋值给容器或者页面。
我们来看一下最简单的TextField组件,组件定义如下 <input jwcid="price@TextField" type="text" value="ognl:picture.price" translator="translator:number,pattern=##.##" validators="validators:min=0" displayName="价格" class="input_text"/> 再看一下TextField中的rewindFormComponent组件方法 protected void rewindFormComponent(IMarkupWriter writer, IRequestCycle cycle) {
//从请求中得到参数值
String value = cycle.getParameter(getName());
try {
//用translator来转换值
Object object = getTranslatedFieldSupport().parse(this, value);
//用validators来验证值
getValidatableFieldSupport().validate(this, writer, cycle, object);
//赋值给容器或者页面
setValue(object);
} catch (ValidatorException e) {
getForm().getDelegate().record(e);
}
}
可以看到在rewindFormComponent中,主要是从请求中取得用户输入的值,然后进行处理,最后赋值给容器或者页面,上面的例子中会调用页面类的getPicture().setPrice(“用户输入的值”)来进行赋值。这样整个表单的提交就可以理解为所有的表单组件读取用户输入的值并赋值给页面的过程。 整个表单提交的详细处理过程如下:
* pageBeginRender() ("rewind"):getRequestCycle().isRewinding()为true * rewind of the form / setting of properties:所有表单组件调用rewindFormComponent来取值赋值 * Deferred listeners (for Submit components):调用Submit组件的listener * Form's listener:调用Form组件的listener * pageEndRender() ("rewind"): getRequestCycle().isRewinding()为true * pageBeginRender() (normal): getRequestCycle().isRewinding()为false * pageEndRender() (normal): getRequestCycle().isRewinding()为false public abstract void setPictures(List<Picture> pictures);
public abstract void setPictureInList();//用于For中的value
public abstract void setPicture(Picture picture);//用于表单创建
public abstract Picture getPicture();
public void pageBeginRender(PageEvent event) {
if(getPicture()==null){
setPicture(new Picture());
}
setPictures(getPictureService().findAll());
}判断picture是否为null并赋值在页面显示和rewind中都是需要的,因为页面显示时,需要调用getPicture().getPrice(),页面rewind时,需要调用getPicture().setPrice(),这两个阶段中的picture都不能为null。但setPictures会在表单提交时被调用两次,在rewind阶段初始化它是没有用处的,所以这时就要对是否rewind进行判断。修改后的代码如下:
public void pageBeginRender(PageEvent event) {
if(getPicture()==null){
setPicture(new Picture());
}
if (!event.getRequestCycle().isRewinding()) {
setPictures(getPictureService().findAll());
}
}这样就可以避免在rewind时对pictures进行不必要的赋值。这里还要提到的一点是页面显示和提交后的页面很可能不是同一个页面类的实例,大家都知道页面类的实例是从实例池取到的,用户打开页面显示表单完后的页面类实例和用户提交表单时的用来rewind的页面类实例不一定是同一个,即使是一个实例,也是被重新初始化过的,不要想当然的认为显示表单后再提交那个实例应该保存原来显示的东西,这个应该理清楚。
声明:JavaEye文章版权属于作者,受法律保护。没有作者书面许可不得转载。
|
|
| 返回顶楼 | |
|
时间:2006-12-31
写得不错!!
|
|
| 返回顶楼 | |
|
时间:2007-01-07
有一个地方没有写对
initialize():页面初始化 是在页面放入池中之前,AbstractPage中的detach方法(解除页面和engine之类的东西的attach)调用的,而不是在页面从池中取出的时候。 public void detach() { Tapestry.addMethodInvocation(Tapestry.ABSTRACTPAGE_DETACH_METHOD_ID); // Do this first,so that any changes to persistent properties do not // cause errors. _changeObserver = null; firePageDetached(); initialize(); _engine = null; _visit = null; _requestCycle = null; } |
|
| 返回顶楼 | |
|
时间:2007-01-07
整个流程不是说从池中取出后,响应请求的,只是说明流程。如果还没有创建这个页面的池,那它会创建这个页面,并响应请求,然后放入池中,那创建时会调用initialize()方法。
源代码: /** * Standard constructor; invokes {@link #initialize()}to configure initial values for * properties of the page. * * @since 2.2 */ public AbstractPage() { initialize(); } |
|
| 返回顶楼 | |
|
时间:2007-01-07
创建页面的时候是调用initialize(),然后再执行其它的操作,我没有从最初的流程来看,是从存在的页面以rewind的这个过程来考虑,Sorry,以后有问题还要多多请教前辈:)
|
|
| 返回顶楼 | |
|
时间:2007-03-12
好文 讲的很清晰 评论也精彩!
能学到真东西 |
|
| 返回顶楼 | |
|
时间:2007-04-05
搂主方便讲解一下Tapestry启动时候服务加载的顺序吗?一切都是从哪里开始的呢?
|
|
| 返回顶楼 | |
|
时间:2007-04-22
我没看懂 为什么在rewindForm的时候会执行2次PageBeginRender哪?应该还是一次呀,只是在我门需要在这种情况考率到是否是2次解析的问题.
public void rewindForm(IForm form) try 436 { 437 page.beginPageRender(); 438 439 form.rewind(NullWriter.getSharedInstance(), this); 440 441 // Shouldn't get this far, because the form should 442 // throw the RenderRewoundException. 443 444 throw new StaleLinkException(Tapestry.format("RequestCycle.form-rewind-failure", form 445 .getExtendedId()), form); 446 } 447 catch (RenderRewoundException ex) 448 { 449 // This is acceptible and expected. 450 } 451 catch (ApplicationRuntimeException ex) 452 { 453 // RequestCycleExceptions don't need to be wrapped. 454 throw ex; 455 } 456 catch (Throwable ex) 457 { 458 // But wrap other exceptions in a ApplicationRuntimeException ... this 459 // will ensure that some of the context is available. 460 461 throw new ApplicationRuntimeException(ex.getMessage(), page, null, ex); 462 } 463 finally 464 { 465 page.endPageRender(); 466 467 _monitor.pageRewindEnd(pageName); 468 469 reset(); 470 _rewinding = false; 471 } 代码上写的很清楚,应该是一次吧,对T我不是很理解,请 |
|
| 返回顶楼 | |
|
时间:2007-04-23
文章表达的是从一个request到response整个提交流程,这个流程中包含rewindForm,而不是单独说rewindForm这个流程,rewindForm里调用了page.beginPageRender();,在rewindForm之后,页面会重新render一次的。
|
|
| 返回顶楼 | |
|
时间:2007-04-23
刚看了原代码 发现确实是执行2次 一次是在Form 的rewind里面 一次在Service 的service 方法里面调用_responseRenderer.renderResponse(cycle);但是这样的话我每次用到pageRender的时候不是都要很小心的 提醒自己是否页面里面会有form提交产生2次解析的问题了吗?这样会不会很累:)
|
|
| 返回顶楼 | |








