JSF2 + IceFaces 2 — получение UIComponent из ViewRoot

Мне трудно решить следующее. Моя проблема довольно проста: я хотел бы выделить красным цветом поля форм, которые вызвали ошибки проверки. Сообщения об ошибках правильно размещаются в FacesContext с помощью строки context.addMessage(...).

Я бы хотел, чтобы моя система была универсальной. Все поля формы, к которым прикреплено сообщение, автоматически выделяются.

Я нашел на этом сайте ссылку на эту прекрасную статью: http://www.jroller.com/mert/entry/how_to_find_a_uicomponent

С его помощью я реализовал PhaseListener для фазы RENDER_RESPONSE, который делает следующее:

@Override
  public void beforePhase(PhaseEvent event) {
    // get context
    FacesContext context = event.getFacesContext();

    // iterate on all the clientIds which have messages
    Iterator<String> clientIdsWithMessages = context.getClientIdsWithMessages();
    while (clientIdsWithMessages.hasNext()) {

      // get the clientId for the field component
      String clientIdWithMessage = clientIdsWithMessages.next();
      // split on ":"
      String[] splitted = clientIdWithMessage.split(":");

      UIComponent component = findComponentInRoot(splitted[splitted.length - 1]);
      if (component != null) {
        Map<String, Object> attributes = component.getAttributes();

        if (attributes.containsKey("style")) {
          attributes.remove("style");
        }
        attributes.put("style", "background-color: #FFE1E1;");
      }
    }
  }

Это отлично работает почти для всех моих применений.

Теперь, когда это становится немного сложно, некоторые из моих форм имеют такой код:

<ice:dataTable id="revisionDocuments" value="#{agendaBean.agenda.revisionsDocuments}" var="revision">
    <ice:column>
        <ice:inputText value="#{revision.sequenceAdresse}" id="revisionSequenceAdresse" />
    </ice:column>
    ....

Сгенерированная форма имеет несколько строк (по одной для каждого объекта списка ревизийДокументы), и каждый элемент имеет уникальный идентификатор (clientId), который выглядит следующим образом:

contentForm:revisionDocuments:0:revisionSequenceAdresse

С 0 менялся на 1, 2, ... для каждой итерации. Следовательно, код, предоставленный для поиска UIComponent из ViewRoot, не работает должным образом. Все поля формы имеют одинаковый идентификатор. Что меня больше удивляет, так это то, что у них есть тот же «clientId» и в FacesContext:

contentForm:revisionDocuments:revisionSequenceAdresse

Я не могу различить, проходя по дереву, вижу ли я правильное поле формы или любое другое.

У кого-нибудь есть подсказка, чтобы решить эту проблему? Или другое предложение реализовать подсветку моих полей? Я должен признать, что мне не очень нравится мой код, я считаю грязным манипулировать viewRoot, как я это делаю, но я не мог найти лучшего решения, чтобы иметь общую подсветку моих полей.

Я использую IceFaces 2.0.2 с JSF-Impl 2.1.1-b04 на JBOss AS 7.0.2.Final.

Заранее спасибо за ответы. С уважением, Патрик


person Wis    schedule 09.12.2011    source источник


Ответы (2)


Вместо этого вы должны применить это на стороне клиента. У вас есть коллекция идентификаторов клиентов с сообщениями. Один из способов — передать эту информацию в JavaScript и позволить ему сделать свою работу. Вы можете найти пример такого PhaseListener в этой статье: Set сфокусировать и выделить в JSF.

Однако, начиная с JSF 2.0, существует другой способ без использования PhaseListener. Появилась новая неявная переменная EL, #{component}, которая ссылается на UIComponent экземпляр текущего компонента. В случае компонентов UIInput существует Метод isValid() . Это позволяет вам сделать что-то вроде:

<h:inputText styleClass="#{component.valid ? '' : 'error'}" />

с этим в файле CSS:

.error {
    background: #ffe1e1;
}

(да, вы также можете сделать это в атрибуте style, но смешивать стиль с разметкой — плохая практика)

Чтобы абстрагироваться от этого (чтобы вам не нужно было повторять это при каждом вводе), вы можете просто создать композитный компонент для этого что-то вроде <my:input>.

person BalusC    schedule 09.12.2011
comment
Что ж, я уже видел статью, на которую вы ссылаетесь, но не смог ее использовать, потому что я использую IceFaces. Код Javascript выполняется один раз при загрузке страницы (когда на странице еще нет ошибок). Затем, когда возникают ошибки, отображается только часть страницы (в основном ‹h:messages› HTML ‹div›). Код JS не выполняется, когда печатаются ошибки. Кстати, Рафаэль задал тот же вопрос в комментарии к вашей статье. Не могу найти ответ на свой вопрос с Richfaces. - person Wis; 09.12.2011
comment
Просто позвольте IceFaces обновить/перерендерить частичную часть, содержащую также код JS. - person BalusC; 09.12.2011
comment
Чувак, я не скажу, что ты спасаешь мне жизнь, но ты действительно спасаешь мои выходные. Я выбрал версию javascript, она очень универсальна и не требует никакого кода для каждого ввода (посмотрите только сейчас ваше редактирование о составном компоненте, я попробую, если у меня будет время). Большое спасибо ! - person Wis; 09.12.2011
comment
Что ж, еще один вопрос, он работает для добавления цвета в поле, но я не могу найти решение для удаления цвета, когда поле больше не вызывает ошибку проверки. Следовательно, количество полей, имеющих цвет, никогда не уменьшится. Должен ли я анализировать полный DOM, чтобы удалить все основные классы? это кажется крайне неэффективным... - person Wis; 09.12.2011
comment
Отлично работает с onblur=clearHighlight(this);, вызывает небольшой фрагмент JS, еще раз спасибо! - person Wis; 09.12.2011
comment
У меня есть дополнительная небольшая проблема. Я попробовал ваш совет с ajax-обновлением, добавив ‹script› на свою страницу в: ‹ice:panelGroup rendered=#{agendaBean.hasValidationErrors}›. Эта функция проверяет в FacesContext, если getClientIdsWithMessages() что-то возвращает. Не работает случай, когда я отправляю форму с ошибкой -> появляется подсветка. Затем измените значение, но все равно с ошибкой. Из-за онблюра блики исчезают. Когда я отправляю, javascript не выполняется повторно. Однако вызывается getHasValidationErrors (отлавливается отладчиком). Любое предложение выполнять JS при каждом запросе AJAX? - person Wis; 15.12.2011
comment
Я понятия не имею, как это сделать для IceFaces, так как я им не пользуюсь. Например, PrimeFaces имеет для этого <p:outputPanel>, а RichFaces, например, имеет для этого <a4j:outputPanel>. Эти компоненты автоматически обновляются при каждом запросе ajax. IceFaces должен иметь аналогичный компонент. - person BalusC; 15.12.2011

Для полноты, вот решение, которое я наконец нашел, чтобы выделить поля, которые имеют сообщения об ошибках с IceFaces 2.0.2:

Основная идея точно такая же, как предложенная BalusC на http://balusc.blogspot.com/2007/12/set-focus-in-jsf.html

Фрагмент кода, который мне пришлось изменить в IceFaces, — это небольшой вызов Javascript:

<script>
    setHighlight('${highlight}');
</script>

Я не смог найти ни одного компонента IceFaces, который перерисовывался бы при каждом вызове JS. Я обнаружил, что размещение скрипта в PanelGroup работает в большинстве случаев. Однако был случай, который не сработал:

отправка формы с ошибками вызывает JS. затем повторная отправка формы с ошибками в том же поле, что и предыдущая проверка, НЕ запускает JS. затем повторная отправка формы с любым полем ошибки, в котором больше нет ошибок, запускает JS. затем повторная отправка формы с любым полем без ошибок, имеющим ошибку, запускает JS.

По какой-то причине IceFaces не отображает группу panelGroup, содержащую JS, когда набор полей с ошибками одинаков между двумя вызовами.

Я пытался использовать Javascript API с таким кодом, как Ice.onAsynchronousReceive(), используя библиотеку Prototype для присоединения события к AJAX-завершению commandButton, но не имел большого успеха. Некоторые из моих тестов могли работать (с ошибками, но выполняли свою работу), и я мог наблюдать подобное поведение.

Вот трюк, который я наконец использовал (уродливый, но работающий):

<ice:panelGroup>
    <script type="text/javascript">
        var useless = '#{testBean.time}';
        setHighlight('${highlight}');
    </script>
</ice:panelGroup>

Функция getTime() просто возвращает текущую метку времени. В этом случае значение всегда отличается и запускает выполнение JS при любом запросе AJAX.

К сожалению, у IceFaces нет полезного атрибута "oncomplete" RichFaces, о чем я очень сожалею в данном случае.

Уродливое решение, но забавное и работающее.

person Wis    schedule 16.12.2011