Реализуйте <o: graphicImage> вместо значка в <p: menuitem> из <p: megaMenu>

Хорошо, название немного сбивает с толку, но я не могу понять, как изложить мою проблему в одном предложении.

Итак, вот в чем дело: мне нужно каким-то образом реализовать компонент graphicImage из Primefaces (или даже лучше, Omnifaces, поскольку я загружаю изображения из базы данных и уже использую o:graphicImage в приложении) внутри компонента MenuItem, чтобы отобразить его в компоненте MegaMenu, который создается программно с помощью DefaultMenuModel, созданного в компоненте поддержки. Кроме того (как я упоминал выше), изображения загружаются из базы данных, поэтому я думаю, что решение с загрузкой этих изображений (иконок) через CSS не применимо, так как изображения являются динамическими.

Я перепробовал много подходов, но (излишне говорить) я, к сожалению, потерпел неудачу:-( . У меня нет опыта написания полного стека пользовательского компонента Primefaces, поэтому я попытался с каким-то базовым пользовательским рендерером для компонента MegaMenu. Например, я попытался использовать код из компонента o:graphicImage (encodeBegin и encodeEnd) и реализовать его в моем CustomMegaMenuRenderer (который расширяет MegaMenuRenderer) в переопределенном методе encodeMenuItemContent. Я использовал небольшой хак для передачи выражения EL (например, #{imageStreamer.getImage(23)}, где 23 - это фактический идентификатор изображения), передавая эту строку в качестве значения поля 'icon' компонента MenuItem в модели. Затем в переопределенном encodeMenuItemContent я проверяю, является ли это на самом деле el (а не name icon), я делаю что-то вроде этого:

writer.startElement("img", null);
writer.writeURIAttribute("src", getSrc(context, expresion), "value");
writeAttributes(writer, this, GraphicImage.ATTRIBUTE_NAMES);
writer.endElement("img");

где getSrc (context, expression) реализован так:

private String getSrc(FacesContext context, String expression) throws IOException {
    Resource resource;
    ExpressionFactory ef = context.getApplication().getExpressionFactory();
    ValueExpression dynExpression = ef.createValueExpression(context.getELContext(), expression, Object.class);
    resource = GraphicResource.create(context, dynExpression, null);
    return context.getExternalContext().encodeResourceURL(resource.getRequestPath());
}

Но, к сожалению, getSrc (context, expression) не может вернуть строковое содержимое, как это происходит внутри обычно созданного компонента o:graphicImage.

Я также попробовал какой-то другой подход, такой как программный экземпляр компонента graphicImage (из Primefaces или Omnifaces) в моем рендеринге, а затем вызвал метод encodeAll для объекта, но он также не удался...

GraphicImage gi = (GraphicImage) application.createComponent(GraphicImage.COMPONENT_TYPE);
gi.getAttributes().put("value", expression);
gi.encodeAll(context);

Итак, я пришел к выводу, что мне не хватает чего-то действительно большого там. Я пытался найти что-то похожее на SO и Google, но не удалось. Итак, вот мои реальные вопросы здесь (при условии, что мои подходы плохие):

  1. Можно ли в событии создать экземпляр каждого компонента primefaces/omnifaces внутри пользовательского рендерера и заставить его работать так, как он обычно работает (поэтому он отображает себя так, как ожидалось)? Я думаю, что это был бы самый простой способ заставить мой рендер работать так, как хотелось бы...
  2. Должен ли я идти вперед с этим подходом "Custom Renderer" или это бесполезно в этом случае?
  3. Должен ли я сделать пользовательский класс MenuModel, MenuItem или MegaMenu, чтобы создать описанную выше функциональность?
  4. И, наконец, какой теоретически самый простой способ добиться этого и каков "лучший" способ достичь этого (так что это согласуется с дизайном компонентов простых лиц, как на самом деле это "должно быть" сделано)?

Также стоит упомянуть (но я думаю, что это выходит за рамки), я использую JSF MOJARRA 2.2.9, Primefaces 5.1, Omnifaces 2.0 и Weld на Tomcat 8.

Заранее спасибо.

1 ответ

Решение

Насколько я понимаю, вы пытались использовать что-то вроде:

<p:menuitem ... icon="#{imageStreamer.getImage(23)}" />

Это действительно не сработает. Он будет оценен немедленно, когда средство визуализации получит его. Это тоже не как <o:graphicImage> работает. Это откладывает оценку до момента, когда браузер действительно должен запросить изображение.

Лучше всего вместо этого передать идентификатор изображения:

<p:menuitem ... icon="23" />

(которое может быть безопасно выражением типа icon="#{bean.iconId}" )

Затем вы можете сделать это следующим образом:

@Override
protected void encodeMenuItemContent(FacesContext context, AbstractMenu menu, MenuItem menuitem) throws IOException {
    ResponseWriter writer = context.getResponseWriter();
    String imageId = menuitem.getIcon();
    Object value = menuitem.getValue();

    if (imageId != null) {
        ValueExpression expression = Components.createValueExpression("#{imageStreamer.getImage(" + imageId + ")}", Object.class);
        GraphicResource resource = GraphicResource.create(context, expression, null);
        String src = context.getExternalContext().encodeResourceURL(resource.getRequestPath());
        writer.startElement("img", null);
        writer.writeURIAttribute("src", src, null);
        writer.endElement("img");
    }

    // ...
}

(Вы были довольно близки, к сожалению, вы не показали, как вы создали expression и как именно это не удалось, так что точная причина может быть точно определена)

Другие вопросы по тегам