简体   繁体   中英

How to Set View Model Class Programmatically when Creating Components

I am experimenting with ZK framework and look for ways to load a zul-defined view programmatically via a utility class that uses Executions to load a view definition programmatically.

I do however fail to find a way to set the view model class programmatically, which would be very convenient and would allow some very simple way of reusing a view with different view model classes.

Ie the code to load the view looks like this:

    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
        );
    }

I thought about "forcing" the view model by setting annotations programmatically on the top component info from the page definition, like so:

    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
        );
    }

This did not work however. Maybe it was too late in the process. Or there is some really simple way of doing this but I missed it. Or maybe I should "apply" some BindComposer, but I am not sure how to do that.

Any helpful idea would be great!

Just to make sure I've understood:

  • You have some zul fragment (such as a reusable UI structure)
  • This fragment uses the MVVM pattern, but you want to choose a viewModel object when you are instantiating that fragment, instead of declaring a class name in the zul viewModel="@id()@init()" attribute
  • You want to initialize that fragment from a java class that has access to the UI (using Execution#createComponents to load the fragment and attach them to the page)

Does that sound correct?

If that's the case, you can make this way simpler The attribute can be written as viewModel="@id('yourVmId')@init(aReferenceToAnAlreadyInstantiatedObject)" .

Important note here: Notice that I have NOT put quotes around the object in the @init declaration. I'm passing an actual object, not a string containing a reference to a class to be instantiated.

When you invoke execution.createComponents() you may pass a map<String, Object> of arguments to the created page. You can then use the name of the relevant passed object when you create bind the VM.

have a look at this fiddle (bit rough, but it should make sense): 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);  

FYI if you are using ZK shadow-elements, you can also pass that object to the fragment from an apply with a source in pure MVVM pattern. The <apply> shadow element for example can pass objects to the created content with a variable name, and you can use that variable name when initializing the VM.

Regarding BindComposer: You need to instantiate BindComposer up to ZK 7.X

In ZK 8.X and above, BindComposer will be instantiated automatically when you use the viewModel="..." attribute on a ZK component.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM