【问题标题】:p:selectOneMenu doesn't render custom content via p:column on List<String>p:selectOneMenu 不会通过 List<String> 上的 p:column 呈现自定义内容
【发布时间】:2015-12-20 13:07:15
【问题描述】:

我正在尝试使用 Primefaces 5.2 selectOneMenu 来显示图像及其文件名。这是我的 xhtml 目前的样子:

<h:form>
<h:panelGrid id="createPanelGrid" columns="2">
    <p:outputLabel value="Service Logo:" />
    <p:selectOneMenu value="#{imageBean.selectedImage}" var="l">
        <f:selectItem itemLabel="Select a logo" itemValue="" />
        <f:selectItems value="#{imageBean.imageList}" var="logo" itemLabel="#{logo}" itemValue="#{logo}" />
        <p:column>
            <p:graphicImage value="#{imageBean.imageFolder}/#{l}" style="max-width:50px;max-height:50px;" />
        </p:column>
        <p:column>#{l}</p:column>
    </p:selectOneMenu>
</h:panelGrid>

ManagedBean (imageBean) 有

public List<String> getImageList () {
    List<String> imageList = new ArrayList<String>();
    File[] files = absoluteImageFolder.listFiles();
    for (File file : files) {
        imageList.add(file.getName());
    }        
    return imageList;
}

private String selectedImage;

public String getSelectedImage() {
    return selectedImage;
}

public void setSelectedImage(String selectedImage) {
    this.selectedImage = selectedImage;
}

但是,图像并没有呈现在网页上,只是文件名(我会发布截图,但我没有足够的声誉)。我没有得到两列(首先是图像,然后是文件名),我只得到文件名本身。

当我将文件名字符串包装到 POJO 中并使用转换器时,它可以工作 - 但仅使用字符串就不行。

我怎样才能让它只使用字符串?

【问题讨论】:

  • 日志中没有 404 - 当我在文件名字符串周围使用包装类时,相同的 URL 也可以工作。如果我在日志中找到了可以解释的线索,我就不会发布了。
  • 对不起,我不是说日志中没有线索,只是我没有发现任何线索。你能给我一个提示我在找什么(除了404s,我检查过)?此外,让我感到困惑的是,在服务器端进行更改(添加包装类)使其工作。
  • 不,所需的 元素不会出现在 HTML 输出中 - 它被省略了。相反,我只得到文本(文件名)。如果我使用包装类,我得到 标记没有问题。

标签: jsf jsf-2 primefaces selectonemenu


【解决方案1】:

原因是 Primefaces 库正在检查 selectOneMenu 组件的 var 属性中的 bean,但是您提供的 String 对象不是 beans。所以库根本不呈现任何列覆盖。您将需要 var 属性中的 bean(包装器)和 converter 属性中的相应转换器。

【讨论】:

  • 是的,这就是对我有用的解决方法 - 我只是希望有一种更简单的方法......
  • 这就是问题所在,没有解决方法,因为没有解决方法。这就是将图像放入 Primefaces selectOneMenu 的方式。如果我的回答澄清了您的问题,请接受我的回答。要将答案标记为已接受,请单击答案旁边的复选标记以将其从空心切换为绿色。谢谢。
  • 所以字符串不是 POJO?
  • 我指的是 bean,而不是 pojo。
  • 但它与字符串相关联而不是与对象相关联,所以如果我想将对象传递给 bean,我该怎么做?
【解决方案2】:

SelectOneMenuRenderer source code 证实了这种尴尬的行为(行号匹配 5.2):

260            if(itemValue instanceof String) {
261                writer.startElement("td", null);
262                writer.writeAttribute("colspan", columns.size(), null);
263                writer.writeText(selectItem.getLabel(), null);
264                writer.endElement("td");
265            } 
266            else {
267                for(Column column : columns) {
268                    writer.startElement("td", null);
269                    renderChildren(context, column);
270                    writer.endElement("td");
271                }
272            }

因此,如果项目值是 String 的实例,则通过 &lt;p:column&gt; 的自定义内容将被完全忽略。这确实没有任何意义。直观的预期是自定义内容通过var 属性和/或&lt;p:column&gt; 子项的存在来切换。您最好向 PrimeFaces 人员报告问题以解释/改进这一点。

除了提供非String 类型的项目值之外,解决方法是使用自定义渲染器覆盖SelectOneMenuRenderer,该渲染器将String 包装在另一个对象中,该对象恰好在其返回完全相同的值toString(),如StringBuilder。这样渲染器就会被愚弄,认为这些值不是String 的实例。很高兴他们没有检查instanceof CharSequence

public class YourSelectOneMenuRenderer extends SelectOneMenuRenderer {

    @Override
    protected void encodeOptionsAsTable(FacesContext context, SelectOneMenu menu, List<SelectItem> selectItems) throws IOException {
        List<SelectItem> wrappedSelectItems = new ArrayList<>();

        for (SelectItem selectItem : selectItems) {
            Object value = selectItem.getValue();

            if (value instanceof String) {
                value = new StringBuilder((String) value);
            }

            wrappedSelectItems.add(new SelectItem(value, selectItem.getLabel()));
        }

        super.encodeOptionsAsTable(context, menu, wrappedSelectItems);
    }

}

为了让它运行,在faces-config.xml注册如下:

<render-kit>
    <renderer>
        <component-family>org.primefaces.component</component-family>
        <renderer-type>org.primefaces.component.SelectOneMenuRenderer</renderer-type>
        <renderer-class>com.example.YourSelectOneMenuRenderer</renderer-class>
    </renderer>
</render-kit>

【讨论】:

  • 哇...非常感谢!我永远不会想到这种行为是故意的……再次感谢您提供了很好的解决方法!如果您在我的示例中拥有像 &lt;f:selectItem itemLabel="Select a logo" itemValue="" /&gt; 这样的“非项目”,则在使用(非字符串)POJO 时,解决方法会出现一个小故障 - 您会收到 PropertyNotFoundException,因为在 StringBuilder 对象上找不到 POJO 的属性.我的(懒惰的)修复是在渲染器中使用if (value instanceof String &amp;&amp; !"".equals((String) value)) {
  • 不客气。至于故障,您应该使用itemValue="#{null}"。另见stackoverflow.com/questions/11360030/…
  • 啊,谢谢 - 我会用它。 BTW:我已经在 Primefaces 论坛发帖询问过这个问题,让我们看看他们怎么说。
  • 我看的越多,我就越怀疑 Primefaces 中字符串的这种特殊行为仅适用于添加非选项的情况。遗憾的是,如果您使用#{null} 作为 itemValue,则第 260 行中的条件为 false,因此您转到 else-branch。然后,这会使用空文件名呈现丢失的图像。我假设我必须为 null 和/或空字符串添加特殊处理。从您在这里的回答 [stackoverflow.com/a/11397505/1685666] 我认为这不是标准的 JSF 行为。哦,好吧。
  • 但它与字符串相关联而不是与对象相关联,所以如果我想将对象传递给 bean,我该怎么做?
猜你喜欢
  • 2020-05-22
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-10-07
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多