0

我对扩展库中的对话框控件有疑问:

我创建了一个 java 自定义控件,它可以搜索一些视图、收集一些数据并显示它。如果我将它放在 XPage 上,这很好用。

但我想在对话框中显示数据,所以我使用了扩展库中的对话框控件。在没有任何配置的情况下使用 Dialog 控件也可以正常工作,但是每次打开对话框时,我的控件都需要一些时间来搜索视图并显示数据。因此,为了减少用户的等待时间,我想使用"keepComponents="true"来自对话控制。

现在,如果我第一次打开对话框,一切都是完美的,但是如果我第二次打开它,它会显示第一次打开的内容以及来自我的 controlRenderer 的错误,它告诉我它无法从控件中获取 viewName . 每次我打开和关闭对话框时都会出现此错误。

我在 OpenNtf 上找到了一个帖子,来自某人在使用此选项时在他的对话框中遇到了多个内容的相同问题,但他没有得到任何问题的答案。这是组件的错误吗?我应该忘记这个选项并将我的数据缓存在一个 bean 中吗?为什么渲染器无法从组件中获取 Viewname?

4

1 回答 1

0

下面的答案假定您问题中的短语“java custom control”是指您开发的 JSF 组件;在 XPages 中,术语“自定义控件”通常指的是自定义控件设计元素的实例,它是 IBM 对“复合组件”的 JSF 概念的实现。

您已声明该组件最初按预期运行,但在后续请求中失败。这通常表明组件的restoreStatesaveState方法没有正确实现。

当为应用程序启用默认序列化选项时,所有组件状态都会在每个请求结束时写入磁盘,并在下一次请求开始时读回内存。这两个操作分别由各个组件的saveState和方法处理。restoreState

例如,假设您定义了一个用于将 HTML 画布标签添加到 XPage 的组件,并决定支持与该元素关联的手势和触摸事件。因此,您的组件类将包含用于存储绑定到这些事件的任何代码的字段:

private String ongesturechange;
private String ongestureend;
private String ongesturestart;
private String ontouchcancel;
private String ontouchend;
private String ontouchmove;
private String ontouchstart;

这些字段中的每一个通常都会有一个关联的“getter”和“setter”方法:

public String getOngesturechange() {
    return getStringProperty("ongesturechange", this.ongesturechange);
}

public void setOngesturechange(String ongesturechange) {
    this.ongesturechange = ongesturechange;
}

当初始化该组件的实例时,与为该组件实例定义的每个属性关联的“setter”方法将传递为该属性定义的值。然后,对于初始页面请求的剩余部分,每个已定义属性的私有字段将存储设置的值。在请求结束时,该saveState方法将这些字段的值写入磁盘。典型的saveState方法类似于以下内容:

@Override
public Object saveState(FacesContext context) {
    Object[] properties = new Object[8];
    int idx = 0;
    properties[idx++] = super.saveState(context);
    properties[idx++] = this.ongesturechange;
    properties[idx++] = this.ongestureend;
    properties[idx++] = this.ongesturestart;
    properties[idx++] = this.ontouchcancel;
    properties[idx++] = this.ontouchend;
    properties[idx++] = this.ontouchmove;
    properties[idx++] = this.ontouchstart;
    return properties;
}

调用super.saveState()执行相同的方法,但使用父类中定义的方法版本。因此,每个组件的磁盘表示本质上是一个嵌套数组:层次结构中的每一层都将其从其父类继承的所有属性存储在数组的第一个元素中,然后将其定义的所有属性存储在其他数组元素中.

当组件树在后续请求中恢复时,每个组件都会使用其restoreState方法来重构其所有字段的值。典型的restoreState方法类似于以下内容:

@Override
public void restoreState(FacesContext context, Object state) {
    Object[] properties = (Object[]) state;
    int idx = 0;
    super.restoreState(context, properties[idx++]);
    this.ongesturechange = ((String) properties[idx++]);
    this.ongestureend = ((String) properties[idx++]);
    this.ongesturestart = ((String) properties[idx++]);
    this.ontouchcancel = ((String) properties[idx++]);
    this.ontouchend = ((String) properties[idx++]);
    this.ontouchmove = ((String) properties[idx++]);
    this.ontouchstart = ((String) properties[idx++]);
}

这分层地读回磁盘上的数据:每个类将一组属性传递给父类,然后将剩余的数组元素分配给保存组件状态时与它们关联的字段。

这个过程提供了一种跨请求维护组件状态的简单方法——每个继承层只需要关注层定义的新属性——但是这些状态维护方法很容易忘记实现。如果组件实现中省略了任一方法,则页面会“忘记”后续请求中的属性值,因为它们要么从未写入磁盘,要么未加载回内存,或两者兼而有之。

false假设这是您的问题的根本原因,当组件位于具有默认 ( ) 值的对话框中时,问题不会发生的原因keepComponents是,默认对话框行为是在对话框关闭。这种行为是出于性能原因:从理论上讲,存储仅存在于用户当前未与之交互的对话框中的组件的服务器端表示并没有任何好处。当对话框再次打开时,一个新的每个子组件的实例都是使用原始属性值创建的。在这种情况下,您的组件不保存其状态并不重要,因为每次使用它时,都会创建一个新实例。但是如果对话框被告知将其子级保留在组件树中,那么现在组件必须正确维护自己的状态......否则它的属性值在每个请求结束时被丢弃,并且后续请求不知道先前的值。

总之,是的,如果数据不太可能在请求之间发生足够的变化以证明在每个事件期间再次获取数据是合理的,那么您正在显示的数据应该缓存在 bean(或数据源)中。但是您所描述的特定行为的原因很可能是因为您的组件实现没有正确维护自己的状态。

于 2013-04-07T06:48:04.710 回答