关于在JSF中开发自定义组件,我很难理解一些事情。出于这些问题的目的,您可以假定所有自定义控件都使用值绑定/表达式(而不是文字绑定),但是我也对它们的解释感兴趣。
我在哪里设置值绑定的值?这应该发生在解码中吗?还是应该解码做其他事情,然后在encodeBegin中设置值?
从值绑定中读取-我何时从值绑定中读取数据与从已提交值中读取数据并将其放入值绑定中?
什么时候针对所有这些形式调用形式的动作侦听器? JSF生命周期页面都提到了发生在各个步骤中的事件,但是当仅调用命令按钮的简单侦听器时,它对我来说并不完全清楚
我已经尝试了几种组合,但是总会以难以发现的错误结束,我认为这些错误来自事件生命周期的基本误解。
JSF规范中有一个很好的图表,它显示了请求生命周期-这对于理解这些内容至关重要。
这些步骤是:
-
恢复视图。 UIComponent树将重建。
-
应用请求值。可编辑组件应实现EditableValueHolder。此阶段遍历组件树并调用processDecodes方法。如果该组件不是像UIData这样复杂的组件,那么除了调用其自己的解码方法之外,它不会做任何其他事情。除了找到其渲染器并调用其解码方法(将自身作为参数传递)以外,decode方法并没有做太多事情。渲染器的工作是获取任何提交的值并通过setSubmittedValue对其进行设置。
-
流程验证。此阶段调用processValidators,它将调用validate。 validate方法采用提交的值,使用任何转换器对其进行转换,使用任何验证器对其进行验证,然后(假设数据通过了这些测试)调用setValue。这会将值存储为局部变量。当此局部变量不为null时,将返回它,而不返回任何对getValue的调用的值绑定中的值。
-
更新模型值。此阶段称为processUpdates。在输入组件中,这将调用updateModel,它将获取ValueExpression并调用它来设置模型上的值。
-
调用应用程序。按钮事件侦听器等将在此处被调用(如果有内存,导航也会被调用)。
-
渲染响应。通过渲染器渲染树并保存状态。
-
如果其中任何一个阶段失败(例如,值无效),则生命周期将跳至"渲染响应"。
-
在大多数这些阶段之后,可以触发各种事件,并适当地调用侦听器(例如在"流程验证"之后的值更改侦听器)。
这是事件的某种简化版本。有关更多详细信息,请参考规范。
我会问为什么您要编写自己的UIComponent。这是一项艰巨的任务,需要对JSF架构有深入的了解才能使其正确。如果需要自定义控件,则最好创建一个具体的控件,以使用等效的渲染器扩展现有的UIComponent(如HtmlInputText一样)。
如果污染不是问题,那么可以使用Apache MyFaces形式的开源JSF实现。
在"调用应用程序"阶段(即最终"渲染响应"阶段之前的最后一个阶段),将调用诸如CommandButton之类的操作侦听器。这在JSF生命周期-图1中显示。
It is the only framework that I've
ever used where component creation is
a deep intricate process like this.
None of the other web frameworks
(whether in the .net world or not)
make this so painful, which is
completely inexplicable to me.
当您考虑目标时,JSF背后的一些设计决策开始变得更有道理。 JSF被设计为可工具化的-它公开了许多用于IDE的元数据。 JSF不是Web框架-它是可以用作Web框架的MVP框架。 JSF具有高度的可扩展性和可配置性-您可以在每个应用程序的基础上替换90%的实现。
如果您只想添加一个额外的HTML控件,那么大多数这些事情只会使您的工作变得更加复杂。
The component is a composition of
several inputtext (and other) base
components, btw.
我假设基于JSP包含/基于工具的页面片段不符合您的要求。
我会考虑使用您的UIComponentELTag.createComponent创建一个具有UIPanel基础的复合控件,并从现有实现中创建其所有子级。 (我假设您正在使用JSP / taglibs并做出其他一些猜测。)如果现有的UIPanel渲染器都没有做此工作,则可能需要自定义渲染器,但是渲染器很简单。
我发现最好的文章是Jsf Component Writing,
至于2我在哪里读取组件中值绑定的值,您有一个类似这样的getter
1 2 3 4 5 6 7 8
| public String getBar() {
if (null != this.bar) {
return this.bar ;
}
ValueBinding _vb = getValueBinding("bar");
return (_vb != null) ? (bar) _vb.getValue(getFacesContext()) : null;
}
</wyn> |
这是如何进入getValueBinding的?
在标签类的setProperties方法中
1 2 3 4 5 6 7 8
| 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.");
}
} |