Wicket“nullable”组件视图

3

我经常使用自定义Wicket组件来呈现模型对象。当对象为空时,会显示特定的div。因此,在组件HTML渲染代码中,我有两个div,一个用于“null”情况,另一个用于“非null”情况,并带有一些其他内部标记。其中一个被显示,而另一个则被遮盖。

<div wicket:id="toDisplayWhenObjectIsNull">
    ...
</div>
<div wicket:id="toDisplayWhenObjectIsNotNull">
   <span wicket:id="label">...</span>
   <table wicket:id="table">...</table>
   ...
</div>

我面临的问题是Wicket强制我完全构建这两个div,即使模型对象为null。在所有子组件构建(标签、表格等)的调用中,我必须检查是否为空,这很麻烦并容易出错:

X myX = getModel().getModelObject();
Label label = new Label("label",
    myX == null ? null : formatY(myX.getY()));

第一种解决方法是将非空部分拆分为特定的wicket子组件,可以作为自己的类或主组件的内部类;并将该组件插入到“非空”div的位置。但这会使所需的文件数(资源、HTML和Java代码)翻倍。这不是理想的解决方案。
第二种通用解决方法是创建一个“装饰器”组件来封装任何其他组件,并检查其模型对象是否为空。如果组件为空,则显示标准div;否则,它将依赖于装饰的组件。我尝试使用边框或复合面板实现此目的,但无法使其正常工作。我想实现的效果类似于这样:
// Client code, Java
ViewXPanel xpanel = new ViewXPanel("xpanel", new Model<X>(x));
add(xpanel);

// HTML
<div wicket:id="xpanel"/>

或者,必要时使客户端负责展示组件的“可空性”,在客户端代码中使用类似以下代码:

// Client code, Java
ViewXPanel xpanel = new NullableDecorator(?, ViewXPanel(...));
add(xpanel);

2
你尝试过使用Fragment吗?它们可能适用于这个问题。 - biziclop
不,仔细看这些可能是解决方案的一部分。我会试一试。 - Laurent Grégoire
我在想这个问题是否可以用Border来解决?虽然这是一个常见的情况,但我从未需要过通用的解决方案。要么很容易只需创建两个div元素,要么就更复杂一些,需要创建一个包装组件。无论如何,我敢打赌你可以创建一个ShowOneBorder(Component1, Component2, IModel<?>),它可以正确地添加它们并使用onConfigure()修改其中一个以使其可见。 - jbrookover
3个回答

1
为了能够显示 null 值,我创建了自己的 Label 子类,并覆盖了 onComponentTagBody() 方法,用占位符替换空字符串。该方法如下:onComponentTagBody()
/**
 * Since the converter is not invoked for null values, override this so that 
 * empty string can be replaced with null display value.
 */
@Override
public void onComponentTagBody(MarkupStream markupStream, ComponentTag openTag)
{
    String value = getDefaultModelObjectAsString();
    replaceComponentTagBody(markupStream, openTag, 
                            value.isEmpty()? "N/A": value);
}

这将使得使用成为可能。
// Client code, Java
add(new MyLabel("label");

// HTML
<div wicket:id="label"/>

当然,您需要根据自己的本地化偏好进行自定义设置,但我希望您可以从提供的示例中了解到整体情况。


0

嗯,一个解决方法是有效地使用片段,正如biziclop所建议的那样,所以我在这里描述一下供参考,但不标记为“答案”。

ViewX.html:

<wicket:panel ...>
  <div wicket:id="mainPanel"></div>
  <wicket:fragment wicket:id="nullFragment">
    ...markup for NULL case...
  </wicket:fragment>
  <wicket:fragment wicket:id="nonNullFragment">
    <h4><span wicket:id="theName"></span></h4>
    ...other markup for non-NULL case...
  </wicket:fragment>
</wicket:panel

ViewX.java:

public class ViewX extends Panel {
  public ViewX(String id, IModel<X> xmodel) {
    if (x == null) {
      Fragment nullFragment = new Fragment("mainPanel", "nullFragment", null);
      ... eventual markup if needed ...
      add(nullFragment);
    } else {
      Fragment nonNullFragment = new Fragment("mainPanel", "nonNullFragment", null);
      nonNullFragment.add(new Label("theName", new PropertyModel(xmodel, "name")));
      ... other markup ...
      add(nonNullFragment);
} } }

我仍在寻找一个真正通用的解决方案,无论是通过组合、继承还是修饰非空感知的 ViewX 面板。


0

你没有使用推荐的Wicket模式 :(。

而是从模型中取出东西并塞入另一个组件:

X myX = getModel().getModelObject();
Label label = new Label("label", myX == null ? null : formatY(myX.getY()));

...正确使用模型:

Label label = new Label("label", new AbstractReadonlyModel<Y>() {
    public Y getObject() {
        return formatY(getModel().getModelObject().getY());
    }
});

始终以这种方式执行。

除了在组件层次结构的顶层之间切换“toDisplayWhenObjectIsNull”和“toDisplayWhenObjectIsNotNull”之外,无需测试null。


好的,谢谢你的回复,但这并没有回答我的问题:你所指出的正是我试图避免的,因此才有了这个问题... - Laurent Grégoire
似乎我不理解你所说的“我面临的问题是Wicket强制我完全构建这两个div”的意思 - 为什么会这样? - svenmeier

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接