Apache Wicket:在模型更新之前对Ajax请求做出反应

时间:2014-04-17 13:14:29

标签: ajax wicket

我在页面上有许多Wicket组件,它们使用PropertyModel来反映某些bean的属性。使用AjaxFormComponentUpdatingBehavior时,这些组件会在用户更改时通过Ajax自动更新。

当属性发生变化时,我想用我的组件编辑的bean会激活PropertyChangeEvent,这些bean应触发重新呈现侦听这些事件的某些组件(实现PropertyChangeListener):

示例:

  1. 用户使用TextFieldPropertyModel
  2. 编辑AjaxFormComponentUpdatingBehavior
  3. 发送AJAX请求
  4. Wicket将请求发送到AjaxFormComponentUpdatingBehavior
  5. 行为onEvent更新PropertyModel(不幸的是,此方法为final
  6. PropertyModel调用支持bean的属性设置器
  7. 支持bean会触发PropertyChangeEvent
  8. 现在我希望通知所有侦听同一支持bean更改的组件
  9. 行为调用抽象onUpdate,但现在它已经晚了,属性更改事件已经处理完毕。
  10. 由于我的bean不可序列化,因此无法将组件永久注册为事件侦听器。我要么注册代理对象,以某种方式检索要通知的组件,要么注册我的组件临时以获取AJAX请求的范围。

    我想要做的是在加载目标页面之后挂钩Wickets请求周期之前 Ajax行为更新模型,这将导致PropertyChangeEvent。在这里,我可以将每个组件注册为其后台bean(addPropertyChangeListener)上的事件侦听器,以便在需要更新时通知他们。

    然后,在onEvent中,如果之前收到AjaxRequestTarget,则每个组件都可以使用PropertyChangeEvent来自行更新。

    最后,在onDetach中,组件可以从其bean(removePropertyChangeListener)取消注册。

    不幸的是,我发现没有内置的方法可以获得关于Ajax请求的通知#34;在我的Ajax行为的onUpdate方法中,模型已经更新,注册更改侦听器为时已晚。我可以实现自己的行为,但是使用不同的组件选项(文本字段,选择列表等),这是非常有用的。

    我错过了什么吗?

4 个答案:

答案 0 :(得分:1)

我并不完全明白“注册为事件监听器的组件”的含义。你在谈论注册IRequestCycleListener s?

无论哪种方式,也许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(...);
        }
}

活动文件:

答案 1 :(得分:1)

您可以覆盖#getUpdateModel()以返回false,然后在#onUpdate()中执行任何操作,然后再调用getFormComponent()。updateModel()。

答案 2 :(得分:0)

您可以覆盖正在使用的每个组件的onModelChanging并在那里触发PropertyChangeEvent。根据之前调用的onModelChanging文档 模型改变了。

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

答案 3 :(得分: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()));

现在,在Wicket将其分派给组件或行为之前,在处理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还会在AJAX请求完成后实现AjaxRequestTarget.IListener注销自己。