19

关于在 JSF 中开发自定义组件,有几件事我很难理解。出于这些问题的目的,您可以假设所有自定义控件都使用值绑定/表达式(不是文字绑定),但我也对它们的解释感兴趣。

  1. 我在哪里设置值绑定的值?这应该发生在解码中吗?还是应该 decode 做其他事情,然后在 encodeBegin 中设置值?
  2. 从值绑定中读取 - 我何时从值绑定中读取数据,而不是从提交的值中读取数据并将其放入值绑定中?
  3. 与所有这些相关的表单上的动作侦听器何时被调用?JSF 生命周期页面都提到了在各个步骤发生的事件,但是当只调用命令按钮的简单侦听器时,我并不完全清楚

我尝试了一些组合,但最终总是很难找到我认为来自对事件生命周期的基本误解的错误。

4

4 回答 4

20

JSF 规范中有一个非常好的图表,它显示了请求的生命周期——这对于理解这些东西至关重要。

步骤是:

  • 恢复视图。UIComponent 树被重建。
  • 应用请求值。可编辑组件应该实现 EditableValueHolder。此阶段遍历组件树并调用processDecodes方法。如果组件不是像 UIData 这样复杂的东西,除了调用自己的decode方法外,它不会做太多事情。decode方法除了找到它的渲染器并调用它的decode方法,将自己作为参数传递之外,并没有做太多事情。获取任何提交的值并通过setSubmittedValue设置它是渲染器的工作。
  • 过程验证。此阶段调用processValidators将调用validatevalidate方法获取提交的值,使用任何转换器对其进行转换,使用任何验证器对其进行验证,然后(假设数据通过这些测试)调用setValue。这会将值存储为局部变量。虽然此局部变量不为空,但它将被返回,而不是任何对getValue调用的值绑定中的值。
  • 更新模型值。此阶段调用processUpdates。在输入组件中,这将调用updateModel获取ValueExpression并调用它来设置模型的值。
  • 调用应用程序。按钮事件侦听器等将在此处调用(如果有记忆,导航也会如此)。
  • 渲染响应。树通过渲染器渲染并保存状态。
  • 如果这些阶段中的任何一个阶段失败(例如,值无效),生命周期就会跳到渲染响应。
  • 在大多数这些阶段之后,可以触发各种事件,适当地调用侦听器(例如流程验证之后的值更改侦听器)。

这是一个稍微简化的事件版本。有关详细信息,请参阅规范。

我会质疑你为什么要编写自己的 UIComponent。这是一项艰巨的任务,需要对 JSF 体系结构有深入的了解才能正确完成。如果您需要自定义控件,最好创建一个具体控件,该控件使用等效的渲染器扩展现有的 UIComponent(如 HtmlInputText 所做的)。

如果污染不是问题,可以使用 Apache MyFaces 形式的开源 JSF 实现。

于 2008-08-29T15:09:23.110 回答
4

动作侦听器,例如CommandButton,在调用应用程序阶段被调用,这是最终渲染响应阶段之前的最后一个阶段。这在JSF 生命周期 - 图 1中显示。

于 2008-08-29T11:14:15.447 回答
3

它是我用过的唯一框架,其中组件创建是一个如此复杂的过程。没有其他 Web 框架(无论是否在 .net 世界中)使这变得如此痛苦,这对我来说完全无法解释。

当您考虑目标时,JSF 背后的一些设计决策开始变得更有意义。JSF 被设计成工具化的——它为 IDE 公开了大量的元数据。JSF 不是一个 Web 框架——它是一个MVP框架,可以用作一个 Web 框架。JSF 具有高度的可扩展性和可配置性——您可以在每个应用程序的基础上替换 90% 的实现。

如果您只想插入一个额外的 HTML 控件,那么大多数这些东西只会让您的工作变得更加复杂。

顺便说一句,该组件是几个 inputtext (和其他)基本组件的组合。

我假设基于 JSP 包含/工具的页面片段不符合您的要求。

我会考虑使用您的UIComponentELTag.createComponent来创建一个具有 UIPanel 基础的复合控件,并从现有实现中创建它的所有子控件。(我假设您正在使用 JSP/taglibs 并进行其他一些猜测。)如果现有的 UIPanel 渲染器都没有完成这项工作,您可能需要一个自定义渲染器,但渲染器很容易。

于 2008-08-29T17:02:07.393 回答
1

我发现的最好的文章是Jsf Component Writing,至于 2 我在哪里读取组件中值绑定的值,你有一个看起来像这样的 getter


public String getBar() {  
     if (null != this.bar) {  
         return this.bar ;  
     }  
     ValueBinding _vb = getValueBinding("bar");  
     return (_vb != null) ? (bar) _vb.getValue(getFacesContext()) : null;  
}
  

这是如何进入 getValueBinding 的?在您的标签类 setProperties 方法中

  if (bar!= null) {  
         if (isValueReference(bar)) {  
             ValueBinding vb = Util.getValueBinding(bar);  
             foo.setValueBinding("bar", vb);  
         } else {  
             throw new IllegalStateException("The value for 'bar' must be a ValueBinding.");  
         }  
     }  
于 2008-09-16T14:15:43.007 回答