如何在创建组件时以编程方式设置视图模型 Class

How to Set View Model Class Programmatically when Creating Components

我正在试验 ZK 框架,并寻找通过实用程序以编程方式加载 zul 定义的视图的方法 class,该实用程序使用执行以编程方式加载视图定义。

但是我确实找不到以编程方式设置视图模型 class 的方法,这将非常方便,并且允许使用一些非常简单的方法重用具有不同视图模型的视图 class es.

即加载视图的代码如下所示:

    public static Component loadComponent(Class<?> modelClz, String zulFile, Component parent, Map<String,Object> params) {
        Execution exec = Executions.getCurrent();
        PageDefinition page = exec.getPageDefinitionDirectly(
            new InputStreamReader(modelClz.getResourceAsStream(zulFile)), 
            null
        );
        return exec.createComponents(
            page,
            // no (previous parent)
            parent,
            params
        );
    }

我考虑过通过在页面定义的顶部组件信息上以编程方式设置注释来“强制”视图模型,如下所示:

    public static Component loadComponent(Class<?> modelClz, String zulFile, Component parent, Map<String,Object> params) {
        Execution exec = Executions.getCurrent();
        PageDefinition page = exec.getPageDefinitionDirectly(
            new InputStreamReader(modelClz.getResourceAsStream(zulFile)), 
            null
        );
        if (!page.getChildren().isEmpty()) {
            ComponentInfo top = (ComponentInfo) page.getChildren().get(0);
            AnnotationMap annotationMap = top.getAnnotationMap();
            String viewModel = "viewModel";
            if (annotationMap==null || !annotationMap.getAnnotatedProperties().contains(viewModel)) {
                // no view model set on top declaration, 
                // force ours
                Map<String,String[]> id = new HashMap<>();
                id.put(null,  new String[]{"vm"});
                top.addAnnotation("viewModel","id",id, null);
                Map<String,String[]> init = new HashMap<>();
                init.put(null,  new String[]{String.format("%s", modelClz.getName())});
                top.addAnnotation("viewModel","init",init, null);
                top.enableBindingAnnotation();
            }
        }
        return exec.createComponents(
            page,
            // no (previous parent)
            parent,
            params
        );
    }

但是这没有用。也许在这个过程中为时已晚。或者有一些非常简单的方法可以做到这一点,但我错过了。或者也许我应该“应用”一些 BindComposer,但我不确定该怎么做。

任何有用的想法都会很棒!

只是为了确保我已经理解:

  • 您有一些 zul 片段(例如可重复使用的 UI 结构)
  • 此片段使用 MVVM 模式,但您想在实例化该片段时选择一个 viewModel 对象,而不是在 zul viewModel="@id()@init( )”属性
  • 您想从可以访问 UI 的 java class 初始化该片段(使用 Execution#createComponents 加载片段并将它们附加到页面)

听起来正确吗?

如果是这样的话,你可以让这个方法更简单 属性可以写成viewModel="@id('yourVmId')@init(aReferenceToAnAlreadyInstantiatedObject)".

此处重要说明:请注意,我没有在@init 声明中将对象放在引号中。我正在传递一个实际对象,而不是包含对要实例化的 class 的引用的字符串。

当您调用 execution.createComponents() 时,您可以将参数的 map 传递给创建的页面。然后,您可以在创建绑定 VM 时使用相关传递对象的名称。

看看这个fiddle(有点粗糙,但应该有道理):https://zkfiddle.org/sample/2jij246/4-Passing-an-object-through-createComponents-as-VM#source-2

    HashMap<String, Object> args = new HashMap<String, Object>();
    args.put("passedViewModel", new GenericVmClass("some value in the passed VM here"));
    Executions.createComponents("./fragment.zul", e.getTarget().getPage(),null, args);  

仅供参考,如果您使用的是 ZK shadow-elements,您还可以将该对象从带有纯 MVVM 模式源的应用程序传递到片段。 例如,<apply> shadow 元素可以使用变量名将对象传递给创建的内容,您可以在初始化 VM 时使用该变量名。

关于 BindComposer: 最多需要实例化 BindComposer 到 ZK 7.X

在 ZK 8.X 及更高版本中,当您在 ZK 组件上使用 viewModel="..." 属性时,BindComposer 将自动实例化。