ActionListener вызывается несколько раз (ошибка?) - Mojarra 2.1.3

У меня есть такая кнопка:

   <h:commandButton 
     disabled="#{mybean.searching}"
     binding="#{mybean.searchButton}"
     actionListener="#{mybean.searchForLicenses}"
     value="Search" />

Когда я отлаживаю, я вижу, что actionListener вызывается сначала дважды, затем трижды, затем щелкает четыре раза и так далее.

Кажется, что при каждой перезагрузке actionListener регистрируется еще раз.

Я использую Mojarra 2.1.3 (также пробовал 2.0.6) и Tomcat 7 с IceFaces.

Связывание происходит так:

private javax.faces.component.UICommand searchButton;

public void setSearchButton(UICommand searchButton) {
  this.searchButton = searchButton;
}

public UICommand getSearchButton() {
  return searchButton;
}

person hugri    schedule 08.11.2011    source источник
comment
Как происходит привязка? Можете ли вы опубликовать get / setSearchButton со всем связанным кодом?   -  person mrembisz    schedule 08.11.2011
comment
Проблема уходит, когда снимаю привязку. @mrembisz: я добавил код привязки (извините, он не отформатирован, потому что я получаю предупреждение о вирусе на странице, препятствующее загрузке редактора).   -  person hugri    schedule 08.11.2011
comment
Когда вы указываете привязку, ваш компонент будет повторно использован, если в области видимости останется удерживающий компонент. В одном компоненте может быть много слушателей действий, поэтому с каждым запросом с вашей командой регистрировался новый слушатель.   -  person mrembisz    schedule 08.11.2011


Ответы (2)


Это может произойти, если вы привязали компонент к компоненту с областью действия сеанса или приложения, а не к компоненту с областью действия запроса. Это просто плохой дизайн. Один и тот же компонент будет повторно использован для нескольких запросов / представлений. Вам нужно поместить bean-компонент в область запроса или полностью избавиться от привязки компонентов.

Обратите внимание, что привязка компонента напрямую к bean-компоненту часто является признаком плохой разработки где-то в коде. Что это такое, функциональное требование и / или проблема, для которой вы подумали, что это решение? Если вы подробно остановитесь на этом, мы сможем предложить правильный подход.

Также обратите внимание, что использование одного слушателя действий также является запахом дизайна. Я ожидал, что searchForLicenses будет обычным методом действий. См. Также Различия между action и actionListener.

person BalusC    schedule 08.11.2011
comment
Я полагаю, вы правы. Тем не менее, для меня не очень очевидно, что вы не можете использовать bean-компонент с ограниченным сеансом в сочетании с привязкой компонентов и actionListener. Почему просто не проверяется, был ли уже добавлен слушатель к компоненту? Вероятно, я здесь думаю не в том направлении, но, поскольку это вполне возможно с точки зрения синтаксиса, это не должно закончиться таким беспорядком ... по крайней мере, имхо. - person hugri; 08.11.2011
comment
Компилятор Java или IDE не может предотвратить плохой дизайн. Это может максимально предотвратить синтаксические или логические ошибки. - person BalusC; 08.11.2011
comment
Полностью согласен, но все же не очевидно, что так работает (для меня). Кроме того, когда я использую область просмотра, разве мы не попадаем в тот же беспорядок, потому что при каждой перезагрузке слушатели повторно добавляются, поскольку bean-компоненты удерживаются, пока пользователи остаются на одной странице, т.е. вид, правда? - person hugri; 08.11.2011
comment
Нет, слушатели не добавляются повторно, потому что компонент будет воссоздан заново. Это, в свою очередь, вызвано ошибкой «курица-яйцо» в частичном сохранении состояния просмотра Mojarra. - person BalusC; 08.11.2011
comment
Я запутался :) правильным поведением было бы повторное добавление слушателей, но это не так из-за ошибки Mojarra? - person hugri; 08.11.2011
comment
Да, использование binding в сочетании с областью просмотра не работает. Для каждого представления восстановления будет создан другой экземпляр bean-компонента. Это не связано с вашей конкретной проблемой, но эта ошибка позволяет вам использовать binding в сочетании с областью просмотра так, как вы ожидаете (что в любом случае не является хорошей идеей; вы также никогда не упоминал о функциональных требованиях, поэтому я никогда не могу предложить правильный способ достижения ваших функциональных требований). - person BalusC; 08.11.2011
comment
Вот один пример, в котором я использую слушателей (он отличается от того, который был задан в моем исходном вопросе, потому что там сложнее заменить его действием): у меня есть мастер для создания объектов (два представления: одно для создания, другое для подтверждения с возможностью перехода. back = ›здесь недостаточно запроса или даже состояния просмотра, мне нужна (?) область сеанса, по крайней мере, очень удобно). Теперь у меня есть лед: selectOneMenu, где я хочу отреагировать на событие valueChange (для отображения / скрытия элементов пользовательского интерфейса), используя свойство valueChangeListener. Я сейчас проверил и увидел, что и здесь события множатся ... - person hugri; 08.11.2011
comment
Я больше спрашивал, зачем вам binding, а не зачем вы использовали actionListener. Но для этой части, если вы вообще не используете аргумент ActionEvent и / или выполняете реальную бизнес-работу в методе, тогда ему вообще не нужно быть слушателем действий. Просто сделайте это void методом без аргументов. - person BalusC; 08.11.2011
comment
К сожалению, функциональные требования все еще не ясны, и почему вы используете binding, также неясно. Теперь, когда вы упомянули о valueChangeListener для отображения / скрытия элементов, это начинает больше походить на то, что вы все еще слишком много думаете о способе ajaxless JSF 1.0 / 1.1. - person BalusC; 08.11.2011
comment
Вероятно, это правда ... В настоящее время я портирую свое приложение с JSF 1 на JSF 2, но начинаю сомневаться, что мой способ использования actionListener и привязки изначально был хорошим дизайном в старом JSF ... - person hugri; 08.11.2011
comment
btw - большое спасибо, я еще немного прочитал, чтобы лучше понять, как все должно работать в JSF 2, по крайней мере, теперь я лучше понимаю, почему не рекомендуется комбинировать bean-компоненты с привязкой к сеансу с привязками компонентов. - person hugri; 08.11.2011
comment
Моя цель использования привязок: у меня сложная форма, в которой выбор значения в раскрывающемся списке должен вызывать или скрывать другие компоненты (текстовые поля, списки ...). Я добился этого с помощью valueChangeListener в раскрывающихся списках, где новое значение (из ChangeEvent) используется для определения того, установлено ли свойство «визуализированный» связанными компонентами в значение true или false. Я до сих пор не знаю, как такое поведение возможно без использования valueChangeListeners и связанных компонентов. Можете ли вы сказать, что в этом случае их можно использовать (потому что нет бизнес-логики, а есть только «логика» пользовательского интерфейса)? - person hugri; 08.11.2011
comment
Вместо этого используйте атрибут rendered в представлении. Например. rendered="#{bean.dropdownValue != 'some'}" - person BalusC; 08.11.2011
comment
Может ли кто-нибудь уточнить комментарий BalusC: это, кстати, в свою очередь вызвано ошибкой куриного яйца в частичном сохранении состояния просмотра Mojarra. В некоторых случаях кажется, что привязка компонентов полезна, и я согласен с hubertg, что jsf должен знать, создал ли он уже конкретный слушатель, и не создавать его снова. Я столкнулся с аналогичной проблемой с icefaces, где в icefaces 1.8.2 привязки компонентов JSF 1.2 и слушатели работали нормально, но теперь в icefaces 2.0 и jsf 2.1 слушатели создаются несколько раз - person ; 20.02.2012

Аналогичная проблема возникает, когда компонент использует binding и validator или valueChangListener, а компонент поддержки имеет область видимости View, Session или Application. Затем соответствующие слушатели вызываются много раз, но не один раз во время запроса (+1 раз с каждым новым запросом).

Одно из возможных решений - переопределить класс jsf AttachedObjectListHolder, который используется для хранения компонента listeners. Текущая реализация просто добавляет новый listener к компоненту, даже если тот же listener уже там. Итак, предлагаемое исправление - проверить, что listener не существует, перед его добавлением.

Подробности исправления вы можете увидеть здесь

person Oleksandr Tsurika    schedule 06.10.2014