2015-03-19 3 views
0

Для рамки форм, в которой я предпочитаю использовать JSF в качестве реального интерфейса интерфейса, я ищу способ информирования родительского компонента, если в дочернем компоненте значение изменяется. Facelet базового «контроля» выглядит следующим образом (тела/головы опущены, так как никто не может запустить его в любом случае без десятка классов):Подписывание PostValidationEvent из динамического дочернего компонента

<xf:input ref="/my/xpath/value"> 
    <xf:label>Label</xf:label> 
</xf:input> 

компонент xf:input, который я разработал, динамически создает реальную составляющую щ (PrimeFaces) в зависимости от типа значения, которое указывает ref="/my/xpath/value". Этот реальный компонент ui создается в событии preRenderView, как это сделано в этом примере. Это обрабатывается следующим способом в родительском управлении

@Override 
public void processEvent(SystemEvent event) throws AbortProcessingException { 

    FacesContext context = FacesContext.getCurrentInstance(); 

    if (!context.isPostback()) { 
     control = createControl(context); 
//context.getApplication().unsubscribeFromEvent(PostValidateEvent.class, getControl().getClass(), this); 
     control.subscribeToEvent(PostValidateEvent.class, this); 
    } 
} 

Фактические элементы управления все имеет программно добавлен обработчик Ajax добавил к нему, что позволяет просто обрабатывать конкретный вход («неявного АЯКС») , Стандартные проверки компонентов JSF обычно применяются, и все это отлично работает.

Проблема/вызов заключается в том, что в этом компоненте «обертка» я хочу получать информацию об изменениях значений после проверки. Моя первая мысль была нам subscribeEvent на динамически добавляемые элементы управления, как это:

control.subscribeToEvent(PostValidateEvent.class, this); 

Подписавшие работает, но на постбэка, NPE брошен в UIComponent (Mojarra 2.2.9), так как wrapped равна нулю в следующем методе

public boolean isListenerForSource(Object component) { 

    if (wrapped instanceof SystemEventListener) { 
     return ((SystemEventListener) wrapped).isListenerForSource(component); 
    } else { 
     return instanceClass.isAssignableFrom(component.getClass()); 
    } 

} 

Это может быть потому, что фактический компонент, кажется, вновь создается, когда данные передаются ан, следовательно, «подписки» теряется.

Регистрация на ViewRoot не работает, так как источником события является всегда ViewRoot, и регистрация на Application проста.

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

Другие вещи, которые я пробовал:

  • Использование valueChangeListeners, но это не работает, либо с большим количеством других проблем (в том числе способов сделать это расширяемый)
  • Использование композитных компонентов с привязкой, но это не удалось, включая их динамически , требующие именования контейнеров, которые конфликтуют с идентификатором, необходимым для остальной части фреймворка, положениями меток, подсказок и предупреждений в xhtml и/или в результате dom
  • Обработчики тегов для управления деревом при их создании

Это все с Mojarra до 2.2.9 (не проверял новые или еще MyFaces)

+0

Компоненты лучше всего создавать во время просмотра времени просмотра. 'PreRenderView' должен быть' postAddToView'. Ты это пробовал? – BalusC

+0

Да, я могу понять это, но все серьезные примеры, которые я нашел (включая сообщения из консалтинга Кеннарда о MetaWidget), делают это так. Хотя сейчас я его ищу, я вижу [тот, который использует postAddToView] (http://www.beyondjava.net/blog/change-jsf-2-2-component-tree-tagdecorators-taghandlers/). Позвольте мне попробовать ... – Kukeltje

+0

@BalusC: Я попытался сменить его на событие «PostAddToView», а затем в «UIComponent» нет «NPC» в «PostValidateEvent», но больше нет события: - (... Существует еще большая разница: динамически добавленный ребенок полностью потерян, по крайней мере, при сбрасывании детей в 'PostRestoreStateEvent'. Его просто нет. Я тогда подумал о добавлении' postValidateEvent' к элементу управления в PostRestoreStateEvent это тот момент, когда мне это действительно нужно. Замечательно это работает, но только один раз. Во втором запросе к тому же компоненту NPE снова появляется. – Kukeltje

ответ

1

Добавление компонента в PreRenderViewEvent работает хорошо. Дело в том, что вы, похоже, не имеете возможности подписываться на события, чтобы выжить в запросе. Фактические компоненты воссозданы (в RestoreViewPhase я предполагаю, что не проверял), а затем подписка на событие все еще существует, только «обернутый» контекст, где он должен быть вызван, пуст.

Добавление события PostValidationEvent в PostRestoreStateEvent этого конкретного компонента (оно единственное в FacesContext.getCurrentInstance().getPartialViewContext().getExecuteIds()) заставляет его срабатывать, как указано в комментариях. Трюк (взломать/обходной путь/...), чтобы избавиться от NPE в следующем запросе, это на самом деле удалить событие снова.

((UIComponent) event.getSource()).unsubscribeFromEvent(PostValidateEvent.class, this); 

Я попытаюсь создать пример без какого-либо PrimeFaces или OmniFaces и посмотреть, что происходит тогда, так как они оба, кажется, обертки вокруг контекста, и я хочу, чтобы убедиться, что они не являются причиной поведения.

Смежные вопросы