Ответ 1
Во-первых, a Converter
никогда не намерен применять "значение по умолчанию".
Независимо от вопроса, что бы вы ни делали в getAsString()
, вы должны гарантировать, что полученный String
может быть преобразован обратно в исходный Object
, когда вы передадите его обратно через getAsObject()
. Ваш конвертер не делает этого. Несмотря на то, что вы вряд ли когда-либо использовали его, технически конвертер необходимо изменить, чтобы преобразовать точную строку "Not available"
обратно в null
. Другими словами, ваш конвертер должен быть сконструирован таким образом, чтобы getAsObject()
и getAsString()
могли успешно передавать друг другу результат в бесконечном цикле и каждый раз возвращать тот же результат.
Что касается конкретного функционального требования для обеспечения значения по умолчанию, а не в Converter
, вы должны сделать это либо в модели, либо в представлении, в зависимости от того, откуда происходит фактическое значение по умолчанию. В вашем конкретном случае вы просто хотите иметь текст/метку заполнителя по умолчанию в пользовательском интерфейсе, когда нет такого значения. Это относится к мнению.
Постановка условного теста в EL, как
#{empty bean.value ? 'Not available' : bean.value}
повсюду в приложении, является безумием.
Я полностью согласен с тем, что это безумие, если вы уже на полпути разрабатываете приложение и имеете сотни из них во всех местах. Однако это не безумие, если вы уже учли это с самого начала. Если вы этого не сделали, тогда, хорошо, выучите урок, кусайте пулю и регулярно исправляйте код соответственно. Достойные IDE имеют regex based find & replace, которые могут помочь в этом. Все были там и делали такое безумие, даже я. Чтобы уменьшить шаблон, оберните его в EL-функцию или файл тегов.
Что касается конкретной проблемы Converter
, не вызываемой, когда значение модели null
, которое я лично полностью согласен с непредвиденным поведением, это когда-либо сообщалось как Моджарра номер 630. Позднее он закрыт как WONTFIX, потому что он вызвал сбои в тестовом случае и после этого лучше будет сообщаться как спецификация спецификации JSF. Это было сделано как спецификация JSF спецификации 475 (во время JSF 1.2 уже). Я проверил MyFaces 2.2.9 на этом, и он также не запускает конвертер и, таким образом, предоставляет ту же проблему.
Однако техническая проблема понятна. Значение null
не имеет разумного getClass()
, поэтому конвертер не может быть просмотрен классом значений таким образом. Он работает только тогда, когда конвертер явно зарегистрирован на компоненте.
<h:outputText value="#{bean.potentiallyNullValue}" converter="stringTrimmer" />
Он должен работать, когда значение представляет собой пустую строку ""
, которая относительно тривиальна для реализации в getValue()
пользовательского рендеринга, расширяющего Mojarra TextRenderer
.
@Override
protected Object getValue(UIComponent component) {
Object value = super.getValue(component);
return (value != null) ? value : "";
}
Однако, когда я попробовал это сам, он все равно не удался. Что получается, конвертер по-классу полностью пропускается, когда значение является экземпляром String
. Он работает только тогда, когда конвертер явно зарегистрирован на компоненте. Скорее всего, это был надзор во время реализации спецификации 131 для JSF 1.2 (до этой версии конвертеры для String.class
вообще не поддерживались, и это проблема только исправлена для декодирования, а не для кодирования).
Это может быть переопределено в том же настраиваемом рендерере (с вышеперечисленным getValue()
override) с нижним переопределением getFormattedValue()
, при котором конвертер явно просматривается.
@Override
protected String getFormattedValue(FacesContext context, UIComponent component, Object currentValue) throws ConverterException {
Converter converter = ((UIOutput) component).getConverter();
if (converter == null) {
converter = context.getApplication().createConverter(currentValue.getClass());
}
return super.getFormattedValue(context, component, "".equals(currentValue) ? null : currentValue, converter);
}
Обратите внимание, что вам не нужно проверять UIInput
, поскольку вы явно зарегистрировали свой собственный рендерер только в javax.faces.Output
/javax.faces.Text
семействе компонентов/типе (т.е. он будет работать только на компонентах <h:outputText>
).
Тем не менее, лучшим решением по-прежнему является создание EL-функции или файла тегов для этого.
<h:outputText value="#{empty bean.value ? 'Not available' : bean.value}" />
<h:outputText value="#{of:coalesce(bean.value, 'Not Available')}" />
<h:outputText value="#{of:coalesce(bean.value, i18n.na)}" />
<h:outputText value="#{your:value(bean.value)}" />
<your:text value="#{bean.value}" />