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>
    ....

Сгенерированная форма имеет несколько строк (по одной для каждого объекта списка revisionsDocuments), и каждый элемент имеет уникальный идентификатор (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.

Заранее благодарю за ответы. С наилучшими пожеланиями, Патрик

2 ответа

Решение

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

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

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

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

.error {
    background: #ffe1e1;
}

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

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

Для полноты, вот решение, которое я наконец нашел, чтобы выделить поля, которые имеют сообщения об ошибках с 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, который я очень сожалею в этом случае.

Гадкое решение, но веселое и рабочее.

Другие вопросы по тегам