Проблема жизненного цикла JSF: дерево компонентов строится до применения атрибутов запроса

У меня есть представление Facelets JSF 2.0, содержащее форму поиска, ui:repeat для отображения результатов и пейджер. Он поддерживается bean-компонентом запроса. Сообщение формы состоит из текущей страницы и критериев поиска. Пейджер показывает ссылки на следующую и предыдущую страницы в зависимости от позиции в наборе данных и количества результатов запроса с критериями поиска из формы. Таким образом, дерево компонентов зависит от атрибутов запроса. Например, я использую атрибут rendered для ссылки на следующую страницу, как это

<h:commandLink action="#{listBean.nextPage}" rendered="#{listBean.hasNextPage}" >...

Когда пользователь щелкает эту ссылку, запрос POST содержит текущую страницу и критерии поиска. Проблема в том, что дерево компонентов уже построено в RESTORE_VIEW. В данный момент, поскольку атрибуты запроса еще не применены, я не могу ни сказать, на какой странице в данный момент находится пользователь, ни сколько записей находится в наборе данных, потому что у меня нет критериев поиска. Итак, на этом этапе listBean.hasNextPage оценивается как false. Кажется, это приводит к тому, что CommandLink исчезает из дерева компонентов. После APPLY_REQUEST_VALUES я могу построить свой запрос на подсчет. Имея эту информацию и текущую страницу, я мог вычислить listBean.hasNextPage. Однако, похоже, он не будет снова оцениваться до RENDER_RESPONSE. Действие вообще не вызывается в INVOKE_APPLICATION. Также нет ошибки, что раздражает.

Это работает при замене rendered на c:if. c:if оценивается в RENDER_RESPONSE только один раз, и по умолчанию компонент находится в дереве на первых этапах. Мне это не очень нравится, потому что в (редком, допустимом) случае, когда количество наборов данных изменяется так, что следующей страницы фактически нет, это все равно вызовет действие, и пользователь окажется на нелегальной странице. Также я понимаю, что использование тегов JSTL в Facelets обычно не рекомендуется. Я не знаю почему.

Есть ли какой-нибудь трюк, чтобы отложить оценку до APPLY_REQUEST_VALUES? Должен быть способ использовать этот атрибут для свойств, зависящих от текущего запроса. Просто к вашему сведению, это приложение с портлетом 2.0 на Liferay с мостом портлета JBoss, но я думаю, что это общая проблема JSF.

Заранее спасибо за любые ответы. Надеюсь, я просто что-то упускаю из виду, я все еще изучаю JSF - мне не так сложно написать пейджер, верно :-)


person Paul Schyska    schedule 24.07.2011    source источник
comment
Это точная копия вашего взгляда? У вас нет # char в ваших выражениях. Также я не уверен, что отображаемый атрибут влияет на включение компонента в дерево. c:if, с другой стороны, оценивается только во время построения дерева, это обработчик тегов, а не компонент. Дерево можно как-то перестроить перед рендерингом (возможно частично).   -  person mrembisz    schedule 25.07.2011
comment
Я написал во время утреннего кофе с головы до ног, спасибо за указатель :-). # находятся в представлении.   -  person Paul Schyska    schedule 25.07.2011


Ответы (1)


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

В период с RESTORE_VIEW до INVOKE_APPLICATION вам должен быть доступен набор данных, который использовался для отображения страницы, на которую только что щелкнул пользователь, а не тот, который будет использоваться для отображения текущего запроса.

Самый простой способ добиться этого — сделать ваш вспомогательный компонент @ViewScoped и хранить ссылку на набор данных через переменную экземпляра. После обратной передачи JSF автоматически сохранит «старый» набор данных и будет использовать его в первой части жизненного цикла. Затем в INVOKE_APPLICATION у вас есть все данные для загрузки следующей страницы и, таким образом, устанавливается «новый» набор данных и логическое значение для hasNextPage.

Затем произойдет RENDER_RESPONSE с новыми значениями и отобразится ваша новая страница.

person Arjan Tijms    schedule 25.07.2011
comment
Арья, спасибо за подробный ответ. Для меня это имеет смысл для этого простого случая (пейджер). Тем не менее, представьте, что я хочу переоценить, действительно ли запрос все еще действителен при выполнении второго запроса. Возможно, размер набора данных изменился после рендеринга страницы, так что следующей страницы больше нет. Я признаю, что это очень сконструированный пример, но я пытаюсь сделать это с помощью JSF. Есть ли какая-то альтернатива, которая будет работать с bean-компонентом с областью запроса? - person Paul Schyska; 25.07.2011
comment
Кроме того, я не уверен, что ViewScope будет работать при рассмотрении нескольких окон/вкладок браузера/кнопки возврата в браузере, потому что это в основном состояние сеанса, верно? По этой причине я стараюсь избегать хранения вещей в сеансе. Чисто основанный на запросе подход будет работать, как и ожидалось, во всех этих ситуациях. - person Paul Schyska; 25.07.2011
comment
ViewScope определенно не является сахаром для состояния сеанса. Это область, которая состоит только между обратными передачами одной и той же страницы и уникальна для этой конкретной обратной передачи. Это абсолютно безопасно для использования между несколькими окнами или вкладками. Вы совершенно правы, избегая хранения данных в области сеанса, насколько это возможно, но опять же, область представления абсолютно не совпадает с областью сеанса. - person Arjan Tijms; 25.07.2011
comment
› Возможно, размер набора данных изменился после рендеринга страницы, так что следующей страницы больше нет. - Но это совершенно нормально. Для первых фаз вам нужны точно такие же данные, которые использовались для рендеринга в предыдущем запросе. Это нужно, среди прочего, для того, чтобы убедиться, что вызов метода действия является законным. Затем в методе действия вы получаете следующую страницу, и, к удивлению, данных больше нет. Теперь вы просто устанавливаете ссылку на null или пустую коллекцию, и когда начинается рендеринг, он просто ничего не отображает. - person Arjan Tijms; 25.07.2011
comment
При желании ваш метод действия может распознавать ситуацию с измененным набором данных (запрос следующей страницы, но больше данных) и добавлять сообщение о лицах, сообщающее пользователю об этом. - person Arjan Tijms; 25.07.2011
comment
› Есть ли какая-то альтернатива, которая будет работать с bean-компонентом с областью запроса? - Я лично думаю, что вам не нужно этого хотеть, но если вы действительно настаиваете, вы можете использовать тег Tomahawk t:saveState. Это в основном хранит данные в одном и том же месте, но, в отличие от помещения всего компонента в область представления, вы можете использовать его для размещения отдельных элементов данных. Но опять же, не бойтесь области видимости. Каждая страница JSF уже использует его, поскольку многие компоненты сами уже хранят состояние в этой области. @ViewScoped просто позволяет вам иметь вспомогательный компонент с той же областью действия, что и компоненты на вашей странице. - person Arjan Tijms; 25.07.2011
comment
Мое быстрое исследование оставило впечатление, что ViewScope живет, пока вы не перейдете к другому представлению (странице). Чтобы он работал как область между запросами (со значениями от последнего запроса до UPDATE_MODEL_VALUES 4 и перезаписываемыми текущими параметрами запроса), каждое свойство в bean-компоненте должно быть установлено в каждом запросе. Я думаю, это выполнимо. Но я не думаю, что это можно сохранить в сценарии с несколькими вкладками. Представьте, что пользователь открывает вторую вкладку, несколько раз просматривает ее, затем возвращается к первой вкладке и снова просматривает. Обе вкладки имеют одинаковую область действия, поэтому результат будет неожиданным. - person Paul Schyska; 26.07.2011
comment
С другой стороны, я буду публиковать текущую страницу при каждом запросе, так что на этот раз она не будет вести себя неожиданно :-). Тем не менее свойство, которое вы описали ранее: Во время RESTORE_VIEW до INVOKE_APPLICATION вам необходимо иметь доступный набор данных, который использовался для отображения страницы, на которую пользователь только что щелкнул, а не тот, который будет использоваться для отображения текущего request. не будет работать с несколькими вкладками. Еще раз спасибо за подробные разъяснения! Я отмечаю это как принятое. - person Paul Schyska; 26.07.2011
comment
Еще есть другая идея: можно ли реализовать следующее с настраиваемой областью JSF: сохранить данные области из последнего запроса до фазы 4 UPDATE_MODEL VALUE, затем отбросить всю область и перестроить ее из привязок. - person Paul Schyska; 26.07.2011
comment
› Но я не думаю, что это можно сохранить в сценарии с несколькими вкладками. - поверьте мне, наверное, никто в мире больше не беспокоится о сценариях с несколькими вкладками, чем я ;) ViewScope на 100% экономит время между вкладками. Он работает с идентификатором представления (GUID), который уникален для данной визуализации и используется для индексации раздела данных, содержащего состояние представления. Если бы это не было сохранено, JSF вообще бы не работал, поскольку, как я уже упоминал, ваши обычные компоненты уже используют это для хранения данных. - person Arjan Tijms; 26.07.2011
comment
›Обе вкладки имеют одинаковую область действия. Нет, это абсолютно не так. ViewScope относится к ЭКЗЕМПЛЯРУ представления. Кажется, вы думаете, что все представления с именем foo.xhtml имеют одну и ту же область видимости, но это не так. Каждый экземпляр (вкладка/окно) foo.xhtml имеет свою собственную область просмотра. Сравните это с областью сеанса, не все пользователи (браузеры), которые что-то с ним делают, разделяют сеанс, верно? Каждый пользовательский экземпляр имеет свою собственную область сеанса (собственный уникальный JSESSIONID). - person Arjan Tijms; 26.07.2011
comment
У меня сложилось впечатление, что это один bean-компонент на просмотр для каждого пользователя. Не знал о механизме идентификатора представления. То есть, по сути, JSF создает новую копию области просмотра, когда обнаруживает новую вкладку? Как он это обнаруживает? Или он может отложить от идентификатора представления, что что-то было отправлено с другой вкладки или после кнопки «Назад» в браузере. - person Paul Schyska; 26.07.2011
comment
›JSF создает новую копию области представления, когда обнаруживает новую вкладку? - Когда вы открываете ссылку в новой вкладке, это происходит через GET-запрос. Каждый запрос без лиц (без обратной передачи) запускает новый экземпляр области просмотра. Обратные передачи сохраняют область действия, но каждая обратная передача также создает новый идентификатор GUID для вновь отображаемой версии. Проверьте значение javax.faces.ViewState в исходном коде HTML отображаемых страниц. Это ключ к состоянию просмотра. - person Arjan Tijms; 26.07.2011