JSF 2.0:Отображение полей формы с исходными данными с использованием bean-компонентов области запроса (для обновления данных сервера, специфичных для текущего компонента).
-
26-09-2019 - |
Вопрос
Вполне нормальный случай:
У нас есть составной компонент «портлет», который имеет два состояния:расширился и рухнул.Портлеты запускаются как развернутые, но пользователь может свернуть их, щелкнув по ним.Это состояние должно быть сохранено в сеансе, чтобы при обновлении пользователем страницы или переходе на новую портлеты запоминали, развернуты или свернуты они.
В этом суть проблемы:Как бы вы на самом деле реализовали такое сохранение состояния в компоненте, который можно вставлять на страницу несколько раз?
Немного предыстории/идей:
Реализацию сохранения состояния для одного портлета можно легко реализовать с помощью bean-компонента с областью действия сеанса.Также можно поддерживать фиксированное количество портлетов, создавая фиксированное количество сеансовых bean-компонентов или объявляя различные свойства в одном bean-компоненте.Однако я не хочу даже упоминать, чем эти подходы плохи.
Моя первоначальная идея заключалась в том, чтобы создать карту (или некоторую объектную структуру) в одном сеансовом компоненте для хранения состояний всех портлетов.Однако я не могу напрямую ссылаться на эти данные из JSF (чтобы JSF их тоже обновлял).Я рассматривал возможность написания специального метода получения/установки для получения/обновления правильного значения на карте, но это невозможно, поскольку во время выполнения методов получения и установки нет идентифицирующих данных.
Однако, вероятно, очевидно, что мне нужно сохранить состояния в сессионный компонент.Я могу довольно легко сохранить состояние, разместив форму с помощью f:ajax
и выполнение специального метода с использованием listener
и представленные данные.Чтобы поддерживать несколько экземпляров компонента, я мог бы использовать (несколько экземпляров) bean-компонент с областью запроса для обработки каждого расширения/свертывания.Однако, чтобы фактически опубликовать данные, идентифицирующие портлет и его состояние, мне нужно сначала вставить их в поля формы во время рендеринга.
Итак, как на самом деле предоставить каждому портлету правильные данные во время рендеринга (на самом деле в этом случае просто укажите логическое значение/перечисление, но рассмотрим случай, когда необходимо обрабатывать больше данных)?
Кажется, что:
h:inputHidden
иh:inputText
не поддерживают установку начального значения, отличного от атрибута значения (который будет указывать на#{portletBean.portletState}
).- Я не могу автоматически загрузить bean-компоненты запроса с правильными начальными значениями во время их создания, поскольку идентификационная информация отсутствует.
И снова такое ощущение, что я что-то упускаю...указатели?
Я полагаю, что по крайней мере одним из ответов будет использование UIComponent
а не составные компоненты, но я бы хотел сохранить это как составной компонент, поскольку он содержит множество необработанных элементов HTML.
Обновлять:
- Eсть довольно похожий вопрос, с предложением использовать bean-компонент с областью просмотра.Однако я не думаю, что здесь это целесообразно.
Решение 3
Я использовал ранее решено взломать вызов метода компонента из JS:
Создал пустую невидимую форму с h:commandButton
и f:ajax
внутри него звонит listener
.
Создана отдельная кнопка для запуска события развертывания/свертывания.С помощью этой кнопки запустите метод javascript, который анимирует развертывание/свертывание и в то же время нажимает скрытую кнопку внутри формы.Все необходимые данные передаются с помощью listener
атрибут.Прослушиватель нацелен на сеансовый компонент, который проверяет/обновляет карту состояний, идентифицируемую идентификаторами клиентов компонента.
Это довольно просто, интересно, почему я не понял этого раньше...
Однако он все еще далек от совершенства и не отвечает на вопрос, как можно инициировать bean-компоненты области запроса со значениями по умолчанию, чтобы использовать их в качестве формы хранения/транспортировки.
Другие советы
У меня есть несколько возможных хаков для этого.Я опубликую их здесь, но я не думаю, что это действительно правильный путь:
Используйте JavaScript для настройки полей формы с правильными данными сразу после рендеринга:
window.addEvent('domready', function() {
var portletState = '#{portletBean.getPortletState(cc.attrs.clientId)}';
// find the field and set the data...
});
Ощущение очень хакерское.Я не хочу идти по этому пути.
Используйте карту атрибутов компонента для хранения данных:
<cc:interface>
<cc:attribute name="portletState" default="#{portletBean.getPortletState(cc.attrs.clientId)}" />
</cc:interface>
Получите доступ к этому из кода:
public void stateChangedCallback(String clientId) {
UIComponent component = FacesContext.getCurrentInstance().getViewRoot().findComponent(clientId);
String state = (String) component.getAttributes().get("portletState");
}
И тоже нехорошо.Также я не уверен, работает ли это (можете ли вы использовать EL в атрибуте по умолчанию).
Использовать карту атрибутов компонента для хранения данных, предоставляя их при вызове компонента:
<portlet:portlet id="specificPortlet">
<f:attribute name="portletState" value="#{portletBean.getPortletState(component.clientId)}" />
</portlet:portlet>
Опять же, очень коряво и дублирует ваш код.
Сделать это можно с помощью taglibs, в теги можно передавать параметры:
Добавьте в вашу библиотеку тегов следующее:
<tag>
<tag-name>date</tag-name>
<source>components/date.xhtml</source>
</tag>
Тогда вот содержимое компонентов/date.xhtml
<ui:composition name="date_template">
<h:panelGrid id="#{id}Panel" columns="1" border="0" cellpadding="0" cellspacing="0">
<rich:calendar immediate="true" id="#{id}" popup="true" datePattern="dd.MM.yyyy"
enableManualInput="true" showApplyButton="true" cellWidth="12px" cellHeight="11px" style="width:100px"
value="#{dateForm.date}" required="#{required}" readonly="#{readonly}" validator="#{dateForm.validateDate}">
<a4j:support event="onchanged" reRender="#{id}Panel,#{reRenderDate}"/>
<ui:insert />
</rich:calendar>
<rich:message for="#{id}" errorClass="error" />
</h:panelGrid>
</ui:composition>
И затем вы используете это как:
<adse:date id="dateTransfert" required="true"
dateForm="#{dossierBean.dateTransfert}"
readonly="false" reRenderDate="montantAncien,montantNouveau,montantEmolument">
<a4j:support event="oninputblur" reRender="dateTransfertPanel,montantAncien,montantNouveau,montantEmolument"/>
</adse:date>
В данном случае передается dateForm, это объект, и в библиотеке тегов мы используем свойства этого объекта (dateForm.date).Также обратите внимание на <ui:insert />, который можно использовать для включения других элементов в XHTML.
Таким образом, вы можете передать контекст для каждого портлета.Вы могли бы даже сделать этот стандарт, используя portletBean в качестве суперкласса, определяющего метод getPortletState().