Apache Wicket - 更新共享特定模型的所有组件的最佳方式

如何解决Apache Wicket - 更新共享特定模型的所有组件的最佳方式

使用 Apache Wicket 9 - 刷新所有 组件 共享的 模型 的最佳方法是什么后台进程?

以下代码有效,但恐怕有我不知道的更好(内置)解决方案。特别是没有为每个具有修改内容Model 调用 visitChildren() :

<wicket:panel>
    <span wicket:id="child"></span><br>
    <a wicket:id="update">update</a><br>
</wicket:panel>

public class Test extends Panel {

    public Test(String id) {
        super(id);

        AtomicInteger i = new AtomicInteger(); // arbitrary content within the model
        IModel<String> model = () -> i.toString();
        add(new Label("child",model).setoutputMarkupId(true));
        add(new AjaxLink<>("update") {
            @Override
            public void onClick(AjaxRequestTarget target) {
                i.incrementAndGet(); // change content within the model
                updateComponents(target,model,getPage());
            }
        });
    }


    // for all components in page use root = getPage()
    public static void updateComponents(AjaxRequestTarget target,IModel<?> model,MarkupContainer root) {
        root.visitChildren(Component.class,(c,ivisit) -> {
            if (c.getDefaultModel() == model) target.add(c);
        });
    }
}

解决方法

Wicket 没有提供任何开箱即用的相关内容(在当前的最新版本中 - Wicket 9.2.0)。

你的实现在我看来相当不错!我也会这样做。

您可以考虑的一项改进是使用您可以全局注册的 AjaxRequestTarget.IListener,即如果您需要在多个地方使用相同的逻辑,则对于整个应用程序。

在 MyApplication#init() 方法中:

getAjaxRequestTargetListeners().add(new AjaxRequestTarget.IListener() {
  @Override public void onBeforeRespond(final Map<String,Component> map,final AjaxRequestTarget target) {
    // map's values are the Components what you have already added to AjaxRequestTarget
    
    // I see your second approach:
    // You can use RequestCycle's MetaData to store flags which types
    // of components/behaviors/models need to be added to the target
    // as well. I.e. in #onClick() do
    // `RequestCycle.get().setMetaData(BEHAVIOR_TYPE,State2Listener.class)` 
    // and here use `Class<?> behaviorClass = `RequestCycle.get().getMetaData(BEHAVIOR_TYPE)`
  }
});
,

虽然我最初的方法有效,但我发现它受到组件模型必须设计为适合比较这一事实的限制。为了克服这个限制,我想展示另一个基于简单自定义行为的解决方案。这样,我们就可以相应地刷新所有必需的组件,而无需对组件模型进行任何假设,如以下示例所示:

<wicket:panel>
    <span wicket:id="child1"/> + <span wicket:id="child2"/> = <span wicket:id="sum"/><br>
    <a wicket:id="update1">update1</a><br>
    <a wicket:id="update2">update2</a><br>
</wicket:panel>

public class Test2 extends Panel {

    // example data class 
    static class Pojo implements Serializable {
        int i1,i2; // some mutable data
    }

    // data instance
    Pojo data = new Pojo();

    // listeners
    public class State1Listener extends Behavior {}
    public class State2Listener extends Behavior {}

    public Test2(String id) {
        super(id);
        
        add(new Label("child1")
                .add(new State1Listener()) // mark as depending on i1
                .setDefaultModel(() -> Integer.toString(data.i1))
                .setOutputMarkupId(true));

        add(new Label("child2")
                .add(new State2Listener()) // mark as depending on i2
                .setDefaultModel(() -> Integer.toString(data.i2))
                .setOutputMarkupId(true));

        add(new Label("sum")
                .add(new State1Listener()) // mark as depending on i1
                .add(new State2Listener()) // and i2
                .setDefaultModel(() -> Integer.toString(data.i1 + data.i2))
                .setOutputMarkupId(true));

        add(new AjaxLink<>("update1") {
            @Override
            public void onClick(AjaxRequestTarget target) {
                data.i1++; // do some update on i1
                
                // we know that i1 was (potentially) modified,// so refresh all components depending on i1
                updateComponentsByBehavior(target,State1Listener.class,getPage());
            }
        });
        
        add(new AjaxLink<>("update2") {
            @Override
            public void onClick(AjaxRequestTarget target) {
                data.i2++; // do some update on i2
                
                // we know that i2 was (potentially) modified,// so refresh all components depending on i2
                updateComponentsByBehavior(target,State2Listener.class,getPage());
            }
        });
    }
    
    // static helper method
    public static void updateComponentsByBehavior(AjaxRequestTarget target,Class<? extends Behavior> behaviorClazz,MarkupContainer root) {
        root.visitChildren((c,ivisit) -> {
            if (!c.getBehaviors(behaviorClazz).isEmpty()) target.add(c);
        });
    }
}
,

根据 martin-g 的回答,这里是对基于后一种 行为 方法的另一种改进。这次使用 RequestCycle.get/setMetaData() 而不是重复调用 visitChildren()。要使用此设置运行 Test2 示例,updateComponentsByBehavior(...) 的调用必须替换为 registerComponentsByBehavior(...) :

public class MyApplication extends WebApplication {

    private static final MetaDataKey<List<Class<? extends Behavior>>> BEHAVIOR_TYPE
        = new MetaDataKey<>() {};

    public static void registerComponentsByBehavior(Class<? extends Behavior> behaviorClazz) {
        // register behavior class in current cycle
        RequestCycle.get().getMetaData(WicketApplication.BEHAVIOR_TYPE).add(behaviorClazz);
    }

    @Override
    public void init() {
        ...
        getRequestCycleListeners().add(new IRequestCycleListener() {
            @Override
            public void onBeginRequest(RequestCycle cycle) {
                // clear list of registered behavior classes for current cycle
                RequestCycle.get().setMetaData(BEHAVIOR_TYPE,new ArrayList<>());
            }
        });
                
        getAjaxRequestTargetListeners().add(new AjaxRequestTarget.IListener() {
            @Override
            public void onBeforeRespond(final Map<String,final AjaxRequestTarget target) {

                List<Class<? extends Behavior>> list =
                    RequestCycle.get().getMetaData(BEHAVIOR_TYPE);

                target.getPage().visitChildren((c,ivisit) -> {
                    for (Behavior b : c.getBehaviors()) {
                        // add components if behavior has been previously
                        // registered in current cycle
                        if (list.contains(b.getClass())) target.add(c);
                    }
                });
            }
        });
    }
}

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其他元素将获得点击?
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。)
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbcDriver发生异常。为什么?
这是用Java进行XML解析的最佳库。
Java的PriorityQueue的内置迭代器不会以任何特定顺序遍历数据结构。为什么?
如何在Java中聆听按键时移动图像。
Java“Program to an interface”。这是什么意思?
Java在半透明框架/面板/组件上重新绘画。
Java“ Class.forName()”和“ Class.forName()。newInstance()”之间有什么区别?
在此环境中不提供编译器。也许是在JRE而不是JDK上运行?
Java用相同的方法在一个类中实现两个接口。哪种接口方法被覆盖?
Java 什么是Runtime.getRuntime()。totalMemory()和freeMemory()?
java.library.path中的java.lang.UnsatisfiedLinkError否*****。dll
JavaFX“位置是必需的。” 即使在同一包装中
Java 导入两个具有相同名称的类。怎么处理?
Java 是否应该在HttpServletResponse.getOutputStream()/。getWriter()上调用.close()?
Java RegEx元字符(。)和普通点?