Навигация по дереву компонентов внутри пользовательского интерфейса: повторение

#jsf-2 #primefaces

#jsf-2 #основные пространства

Вопрос:

У нас есть следующая структура в нашем xhtml:

 <ui:repeat ...>
  ...
  <h:selectOneMenu ...>
  ...
    <p:ajax event="change" update="@parent:@parent" process="@parent:@parent" />
  </h:selectOneMenu>
</ui:repeat ...>
  

В обработчике событий мы получаем доступ к UIComponent, представляющему selectOneMenu . Мы можем перейти к родительскому элементу, представляющему пользовательский интерфейс: повтор. При просмотре дочерних элементов я ожидал бы увидеть все selectOneMenu, видимые на веб-странице, но я вижу только один on . Тот, который вызвал событие изменения.

Как я могу получить доступ к родственным компонентам через компонент ui: repeat?

Мотивация:

Мы в основном создаем сгруппированную таблицу. Т.Е. Таблицу, которая сгруппирована в разделы с данными, которые принадлежат друг другу. Приведенный выше код создает одну группу. Определенные значения в selectOneMenu должны быть выбраны ровно для одной записи в группе. Поэтому, когда изменяется одно значение, мы должны получить доступ ко всем записям в одной группе, чтобы проверить, заполнено ли ограничение, и в противном случае отображать сообщения об ошибках в полях, нарушающих.

Как это выглядит в браузере:

 table: there is another outer repeat for creating the contents of this table
..group 1: content of each group is created by the ui:repeat
....row with selectOnMenu
....row with selectOnMenu
....row with selectOnMenu
....row with selectOnMenu
..group 2: content of each group is created by the ui:repeat
....row with selectOnMenu
....row with selectOnMenu
....row with selectOnMenu
....
  

строка с selectOnMenu

Как это выглядит в обработчике событий:

 UiRepeat --> SelectOneMenu(exactly one)
  

Комментарии:

1. Каждый из ваших h:selectOneMenu компонентов должен быть привязан к серверной модели, заданной его value атрибутом. Вам этого недостаточно или вам нужно также получить экземпляр UIComponent конкретно? Что вы ищете в UIComponent?

2. нам нужен UIComponent, потому что мы хотим прикрепить к нему сообщения проверки. Я также хотел бы понять, нормально ли, что мы видим только один компонент, или мы делаем что-то особенное / неправильное, просто чтобы лучше понять JSF.

3. Запросы Ajax могут отправлять разные части формы. Это то, что вы определяете в своих f:ajax execute="" p:ajax process="" атрибутах or . Поскольку они установлены по умолчанию @this , в process отправляется только current component ( h:selectOneMenu ) , поэтому переключение на него @form может быть ключевым для вашей проблемы. Кстати, используете ли вы h:selectOneMenu with p:ajax по какой-либо конкретной причине? Вы должны использовать либо f:ajax ванильный JSF p:selectOneMenu , либо если хотите сделать все это с помощью Prime.

4. @XtremeBiker атрибут процесса должен охватывать все selectOneMenus, я думаю. Я обновил код в вопросе. Причина смешивания h: и p: не очень конкретна. p:selectOneMenu просто выглядел очень уродливо.

5. Затем продолжайте f:ajax . Это тег для компонентов JSF.

Ответ №1:

Компоненты любят ui:repeat и h:dataTable используют подход, называемый штамповкой, для обработки дочернего содержимого. Это означает, что дочерние компоненты существуют только один раз, независимо от количества элементов в коллекции, на которую ссылается value . Когда компонент обрабатывается, JSF повторно использует этот компонент (ы) для каждого элемента в коллекции (когда вы смотрите на отображаемый HTML-код, вы можете увидеть индекс в идентификаторе). При таком подходе размер дерева компонентов остается одинаковым, если в вашей коллекции один или 1000 элементов.

Приведенное выше является лишь объяснением поведения, которое вы заметили. Что касается вашей проблемы, может быть несколько решений в зависимости от точных требований и способа структурирования ваших данных.

Вы можете выполнить проверку в прослушивателе, который вы указываете в p:ajax теге, запускающем запрос (при условии, что вы можете найти данные для текущей группы в модели). Этот прослушиватель выполняется на этапе 5 жизненного цикла. Если запросы ajax отправляют текущую группу, данные уже должны быть доступны в модели.

Затем вы можете добавить сообщения об ошибках проверки для определенного компонента следующим образом:

 FacesContext ctx = FacesContext.getCurrentInstance();
ctx.addMessage("clientId of Component",
               new FacesMessage(FacesMessage.SEVERITY_ERROR, "Your message", null));
  

Один из способов получить идентификаторы клиентов представленных компонентов — это прослушиватель системных событий для preValidate события на вашем h:selectOneMenu :

 <f:event type="preValidate" listener="#{page.preValidate}"/>
  

Затем в методе прослушивателя вы можете получить доступ к идентификатору клиента текущего компонента (включая индекс текущего элемента!):

 public void preValidate(ComponentSystemEvent event) {
  String clientId = event.getComponent().getClientId();
}
  

Затем этот прослушиватель вызывается для всех представленных компонентов. Кроме того, вам каким-то образом нужно будет сопоставить этот идентификатор клиента с текущим элементом в модели (например, получить индекс родительского ui:repeat компонента).

Выше приведены лишь некоторые основные идеи.

Ответ №2:

Основываясь на вашем поведении и ответе Миши, вы должны использовать

 <c:forEach>
  

Вместо

  <ui:repeat>
  

. ForEach сгенерирует все необходимые вам компоненты.