Русский English Тэги View Sergey Zolotaryov's profile on LinkedIn Вход
Проблема с popup окнами в JSF при использовании t:saveState
Постоянная ссылка 06-04-2007 anydoby java

Ни для кого не секрет, что сохранять состояние между разными views в JSF это проблема. Иногда это решается при помощи скрытых полей (работает только внутри одной формы, если на странице несколько форм, то данные сохраяются только в одной из них), есть ребята, использующие для этого сессию. В общем такие решения могут пройти, если нет требования о поддержке многооконного интерфейса. К счастью, есть более элегантное решение - использование тэга t:saveState из библиотеки Tomahawk. Он позволяет сохранять состояние между разными views, если в каждом view есть t:saveState с одним и тем же бином.

Однако и здесь есть свои нюансы. О них мы поговорим в этой статье.

Предположим, что вам нужно отображать на странице объект message, и на странице несколько форм - естественно вам нужно сохранять объект сообщения пока вы находитесь в пределах этого view. Самый простой способ решения проблемы:


  <t:saveState value="#{messageBean.id}"/>

А теперь добавим условий. Вам необходимо поддерживать несколько popup окон, на которых вы управляете разными сообщениями и эти окна вам нужно контролировать из главного окна, например при помощи javascript. Это значит, что конструкции вроде приведенной ниже использовать нельзя:


<h:actionLink target="_blank" 
              action="messageBean.displayMessage">
  <f:attribute name="message" value="#{message}"/>
</h:actionLink>
потому что окно, открытое таким образом, контролировать из javascript нельзя.

Чтобы открыть окно с другим сообщением, можно воспользоваться проверенным решением - небольшим кусочком javascript и GET вместо POST.


<h:outputLink id="messageLink" 
  onclick="popupMessage('#{message.id}'); return false;" 
    value="javascript:void(0);" >
  <h:outputText value="#{message.id}"/>
</h:outputLink>

и скрипт, который все это открывает:


function popupMessage(id) {
 window.open(
  '${pageContext.request.contextPath}/message.faces?id='
  + id, 'msg_' + id, "");
}

Это позволяет создать управляемое окно из javascript и показывать в разных окнах разные сообщения. Да, в faces-config.xml нужно добавить соответствующие настройки, чтобы id сообщения бралось из параметров запроса:


...
  <managed-property>
    <property-name>id</property-name>
    <value>#{param.id}</value>
  </managed-property>

...

что ж, похоже, что задача решена ... но ... не тут-то было. Если вы откроете всплывающее окошко первый раз, в нем отобразится нужная информация. Если вы нажмете на другую ссылку с другим id вы получите тот же самый messageBean.id, что и в первом запросе. Все дело в том, что t:saveState сохранил messageBean внутри view и еще где-то запомнил, что сейчас вы находитесь на этом view. Следовательно вызов всплывающего окна с тем же view приводит к десериализации уже существующего messageBean, несмотря на то, что в параметре запроса id имеет другое значение - система управления бинами просто не получает возможности сконфигурировать объект как нужно.

Решается проблема до безобразия просто, хоть и не очень красиво - в методе getId() из messageBean нужно добавить следующие строчки:


 ...
  FacesContext ctx = FacesContext.getCurrentInstance();
  ExternalContext eCtx = ctx.getExternalContext();
  Map requestMap = eCtx.getRequestParameterMap();
  String id = requestMap.get("id");
  if (id != null) {
    this.id = id;
  }

  return id;
 ...

Теперь, несмотря на то, что мы получаем десериализованный бин, мы каждый раз имеем новый id (если он конечно присутствует в параметрах запроса) и отдельные всплывающие окна теперь работают совершенно независимо.

Добавить комментарий

Предыдущая статья Проблемы с Oracle Toplink Essentials JPA Следующая статья Установка/удаление Oracle под WindowsXP для чайников