Реализуйте <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, но не удалось. Итак, вот мои реальные вопросы здесь (при условии, что мои подходы плохие):
- Можно ли в событии создать экземпляр каждого компонента primefaces/omnifaces внутри пользовательского рендерера и заставить его работать так, как он обычно работает (поэтому он отображает себя так, как ожидалось)? Я думаю, что это был бы самый простой способ заставить мой рендер работать так, как хотелось бы...
- Должен ли я идти вперед с этим подходом "Custom Renderer" или это бесполезно в этом случае?
- Должен ли я сделать пользовательский класс MenuModel, MenuItem или MegaMenu, чтобы создать описанную выше функциональность?
- И, наконец, какой теоретически самый простой способ добиться этого и каков "лучший" способ достичь этого (так что это согласуется с дизайном компонентов простых лиц, как на самом деле это "должно быть" сделано)?
Также стоит упомянуть (но я думаю, что это выходит за рамки), я использую 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
и как именно это не удалось, так что точная причина может быть точно определена)