2014-04-17 187 views
1

我在頁面上使用PropertyModel來反映一些bean的屬性,有許多Wicket組件。使用AjaxFormComponentUpdatingBehavior時,這些組件在用戶更改時通過Ajax自動更新。Apache Wicket:在模型更新之前響應Ajax請求

當屬性發生改變,我想用我的部件起火編輯PropertyChangeEvent s表示應觸發豆謂監聽這些事件(實現PropertyChangeListener)某些部件的重新呈現:

例子:

  1. 用戶編輯TextFieldPropertyModelAjaxFormComponentUpdatingBehavior
  2. 的AJAX請求被髮送
  3. 檢票調度第r eQUEST的到AjaxFormComponentUpdatingBehavior
  4. 行爲的onEvent更新PropertyModel(不幸的是,這種方法是final
  5. PropertyModel調用支持bean的屬性setter
  6. 的支持bean火災和PropertyChangeEvent
  7. 現在,我希望所有的組件聽同樣的支持豆的變化被通知
  8. 行爲調用摘要onUpdate,但現在是晚了,屬性更改事件已經處理。

由於我的bean不可序列化,所以我無法將組件永久註冊爲事件偵聽器。我需要註冊以某種方式檢索要通知的組件的代理對象,或者註冊我的組件暫時以獲取AJAX請求的範圍。

我想這樣做是掛接到圍牆門請求週期目標頁面已經被加載,但前阿賈克斯行爲更新模型,這將導致PropertyChangeEvent後。在這裏,我可以將每個組件註冊爲其支持bean(addPropertyChangeListener)上的事件偵聽器,以便在需要更新時通知它們。

然後,在onEvent中,如果以前收到PropertyChangeEvent,則每個組件都可以採取措施使用AjaxRequestTarget進行更新。

最後,在onDetach中,組件可以從它們的bean中取消註冊(removePropertyChangeListener)。

不幸的是,我發現沒有內置的方式來獲取「關於Ajax請求」的通知。在我的Ajax行爲的onUpdate方法中,模型已經更新,註冊更改偵聽器爲時已晚。我可以實現自己的行爲,但使用不同的組件選項(文本字段,選擇列表等),這是一項非常努力的工作。

我錯過了什麼嗎?

+0

如果我理解正確,在您的方案中,支持bean負責將「更新自己」事件分發給註冊爲偵聽器的組件?爲什麼不能以相反的方式呢?如下所示:1)更新了bean; 2)發佈了「更新B豆屬性P」的檢票事件; 3)對該屬性感興趣的組件自己更新。 – bernie

+0

我的支持bean(業務層)對Wicket組件(UI層)一無所知,我想保持這種狀態。問題依然如此:一個不在會話中的bean如何通知Wicket組件?我會不知何故需要檢索頁面,並通過ID找到組件? –

+0

我並不是建議業務層應該控制UI。也許我是一個在這裏失蹤的人...我所建議的是,在更新bean之後,Wicket發佈一個Wicket事件,該事件被分發給每個組件,聲明「此Bean的屬性已更新」。然後組件自行決定是否應該刷新。 – bernie

回答

0

這就是我最後想到的。

我將子類別IContextProvider<AjaxRequestTarget, Page>創建爲AjaxRequestTarget對象的自定義提供程序。當請求AjaxRequestTarget時,我使用Wicket的事件機制將它廣播到組件樹。

public class BroadcastingAjaxRequestTargetProvider implements IContextProvider<AjaxRequestTarget, Page> { 
    private final IContextProvider<AjaxRequestTarget, Page> parent; 

    public BroadcastingAjaxRequestTargetProvider(IContextProvider<AjaxRequestTarget, Page> parent) { 
     this.parent = parent; 
    } 

    @Override 
    public AjaxRequestTarget get(Page page) { 
     AjaxRequestTarget target = parent.get(page); 
     page.send(page, Broadcast.BREADTH, new AjaxRequestBegin(target)); 
     return target; 
    } 
} 

AjaxRequestBegin只是一個小的有效載荷對象封裝AjaxRequestTarget

我註冊這個提供在我的Wicket應用程序的init()方法:

setAjaxRequestTargetProvider(new BroadcastingAjaxRequestTargetProvider(getAjaxRequestTargetProvider())); 

現在,當一個AJAX請求被處理的每個組件得到通知,檢票將分派到一個組件或行爲之前。一個組件可以覆蓋onEvent以註冊請求PropertyChangeListener

public void onEvent(IEvent<?> event) { 
    final Object payload = event.getPayload(); 

    if (payload instanceof AjaxRequestBegin) { 
     final AjaxRequestTarget target = ((AjaxRequestBegin) payload).getTarget() 
     AjaxPropertyChangeListener listener = new AjaxPropertyChangeListener(target); 
     target.addListener(listener); 
     getBean().addPropertyChangeListener(listener); 
    } 
} 

private class AjaxPropertyChangeListener implements PropertyChangeListener, AjaxRequestTarget.IListener { 
    private final AjaxRequestTarget target; 

    public AjaxPropertyChangeListener(AjaxRequestTarget target) { 
     this.target = target; 
    } 

    @Override 
    public void propertyChange(PropertyChangeEvent event) { 
     target.add(MyComponent.this); 
    } 

    @Override 
    public void onBeforeRespond(Map<String, Component> map, AjaxRequestTarget target) { 
    } 

    @Override 
    public void onAfterRespond(Map<String, Component> map, IJavaScriptResponse response) { 
     getBean().removePropertyChangeListener(this); 
    } 
} 

注意AjaxPropertyChangeListener也實現AjaxRequestTarget.IListener註銷本身AJAX請求完成後。

1

我不完全理解你的意思是「組件​​註冊爲事件監聽器」。你在說註冊IRequestCycleListener嗎?

無論哪種方式,也許Wicket的inter-component events可以幫助你在這裏。每個組件實現了以下接口:

public interface IEventSink 
{ 
    /** 
    * Called when an event is sent to this sink 
    * 
    * @param event 
    */ 
    void onEvent(IEvent<?> event); 
} 

你可以繼承AjaxFormComponentUpdatingBehavior火災事件的模型更新,像這樣經過:

public class AjaxUpdateEvent { 
    private final AjaxRequestTarget target; 

    public AjaxUpdateEvent(AjaxRequestTarget target) { 
     this.target = target; 
    } 
    public AjaxRequestTarget getAjaxRequestTarget() { 
     return target; 
    } 
} 

public class BeanModifiedEvent extends AjaxUpdateEvent { 
    private final Bean bean; 

    public BeanModifiedEvent(AjaxRequestTarget target, Bean theBean) { 
     super(target); 
    } 
    public Bean getBean() { 
     return bean; 
    } 
} 

public class CustomUpdatingBehavior extends AjaxFormComponentUpdatingBehavior { 

    protected abstract void onUpdate(AjaxRequestTarget target) { 
     Bean bean = getFormComponent().getModelObject(); 
     getComponent().send(getComponent().getPage(), Broadcast.BREADTH, new BeanModifiedEvent(target, bean)); 
    } 
} 

然後,您可以捕捉事件所需要的組件並將它們添加Ajax請求:

public class UserDetailsPanel extends Panel { 
..... 
    @Override 
    public void onEvent(IEvent event) { 
     if(event.getPayload() instanceof BeanModifiedEvent) { 
      // if(whatever) to control whether to add or not 
      AjaxRequestTarget target = ((BeanModifiedEvent) event.getPayload()).getAjaxRequestTarget(); 
      target.add(...); 
     } 
} 

事件DOC:

+0

我的意思是我的bean有像addPropertyChangeListener(PropertyChangeListener)這樣的方法。你的建議是我提到的:Subclassing'AjaxFormComponentUpdatingBehavior'。不幸的是,在'onUpdate'中,模型已經更新,事件已經發送,而且我的組件抓住這些已經太晚了。 –

1

您可以覆蓋#getUpdateModel()返回false,那麼在#onUpdate()做任何你想要調用getFormComponent()之前。的UpdateModel()。

0

您可能會重寫onModelChanging您正在使用的每個組件,並在那裏觸發您的PropertyChangeEvent。根據關於ModelChanging的文檔,在 之前調用模型。

@Override 
protected void onModelChanging() { 
    super.onModelChanging(); 
    oldModelObject = yourComponent.getModelObject(); 
    //fire PropertyChangeEvent 
}