views:

219

answers:

1

Hi,

I am using ZK framework to develop web applications. I am using databinding on components to set and get values. I can register databinding in source ZUL file and also in the method doAfterCompose in page's controller. This method is called during page composition. But now I have to add a new component and it's databinding into existing and composed page. How to create component I know, it is simple but I have a problem to register databinding. Framework throws the exception that I am using unknown data bean.

There is a simple code which should work but it doesn't. This ZUL file describes simple page layout and controller catches events etc. There are used annotations ZkModel and ZkEvents. These annotations aren't part of the framework, they are our enhancement. ZkModel publishes the variable to ZUL file so it is accessible from ZUL file using databinding. ZkEvent registers events on the components and invokes these methods on events. These enhancement is working so there is not the problem.

ZUL file (test.zul):

<?xml version="1.0" encoding="UTF-8"?>
<?variable-resolver class="org.zkoss.zkplus.spring.DelegatingVariableResolver"?>
<?init class="cz.datalite.zk.databinder.DLDataBinderInit" root="winTest" validator="${validator}"?>
<!-- template -->
<?init class="org.zkoss.zk.ui.util.Composition" arg0="/includes/template.zul"?>
<?page title="Test page"?>

<zk xmlns="http://www.zkoss.org/2005/zul"&gt;
    <window id="winTest"  self="@{define(content)}" height="100%" apply="${testController}">
        <button label="OK" id="btn"/>
        <label id="lab" value="text"/>
        <textbox id="txt1" value="@{ctl.bindingValue}"/>
    </window>
</zk>

TestController.java:

package cz.datalite.bpej.evidence;

import cz.datalite.stereotype.Controller;
import cz.datalite.zk.annotation.ZkEvent;
import cz.datalite.zk.annotation.ZkModel;
import cz.datalite.zk.components.textbox.DLTextbox;
import cz.datalite.zk.composer.DLComposer;
import java.util.HashMap;
import java.util.Map;
import org.zkoss.zk.ui.Component;
import org.zkoss.zkplus.databind.Binding;
import org.zkoss.zkplus.databind.DataBinder;
import org.zkoss.zul.impl.XulElement;

@Controller // this class serves as a controller
public class TestController extends DLComposer {

    @ZkModel // this property is published and accessible from ZUL file
    String bindingValue = "there is binding text";

    @ZkEvent( id = "btn" ) // this methods is invoked on "onClick" event on component "btn"
    public void onOk() throws Exception {
        DLTextbox textbox = new DLTextbox();
        textbox.setParent( self );
        setValueAnnotation( textbox, "value", "ctl.bindingValue" );    
    }

    /**
     * Sets the component's annotation to specific value
     * (call eg. setValueAnnotation(comp, "model", "aaa") is corresponding to model="@{aaa}")
     * @param comp defined component
     * @param propName name of property
     * @param annot annotation
     */
    private void setValueAnnotation( XulElement comp, String propName, String annot ) {
        DataBinder binder = ( DataBinder ) comp.getVariable( "binder", false );

        // adds new binding
        Map attrs = new HashMap();
        attrs.put( "value", annot );
        binder.addBinding( comp, propName, annot );

        // if the first bean is fellow then register it ( if it hasn't been used yet then it is not registered. )
        String bean = annot;
        if ( bean.contains( "." ) ) {
            bean = bean.split( "\\." )[0];
        }
        Component fellowBean = comp.getFellowIfAny( bean );
        if ( fellowBean != null ) {
            binder.bindBean( bean, fellowBean );
        }

        // load components value
        Binding bind = (( DataBinder ) comp.getVariable( "binder", false )).getBinding( comp, propName );
        if ( bind != null ) {
            bind.loadAttribute( comp );
        }

    }
}

If I run these two files the application works fine. On onClick event on button is created new textbox and its value is bind to the right property. But now, if I comment textbox component in ZUL file

        <!--textbox id="txt1" value="@{ctl.bindingValue}"/-->

then it stops working. Now there is thrown exception

Cannot find the specified databind bean expression:ctl.bindingValue

org.zkoss.zkplus.databind.DataBinder(DataBinder.java#myGetBeanWithExpression:1004)
org.zkoss.zkplus.databind.DataBinder(DataBinder.java#getBeanAndRegisterBeanSameNodes:988)
org.zkoss.zkplus.databind.Binding(Binding.java#loadAttribute:413)
cz.datalite.bpej.evidence.TestController(TestController.java#setValueAnnotation:58)
cz.datalite.bpej.evidence.TestController(TestController.java#onOk:25)

And that is the problem. I need to be able to create a new component and add its databinding without using the bean in ZUL file. I need to be able to register it from the controller. Can you help me, please? I'll be really grateful.

+2  A: 

Unfortunately, the current DataBinder does NOT support dynamic binding yet (Have to wait Data Binding 2.0, hopefully will available in ZK 5.5).

The current DataBinder implementation will IGNORE all later added Bindings after first getXxx() or setXxx() call. In those methods, the init() method is called on demand and not called again if ever called. In the init() method, basically DataBinder scans through all the Bindings and construct an internal data structure and use those internal data structure afterward.(That is why the later added binding is not seen by the DataBinder)

Hope this clarify your issue.

henrichen
Thanks a lot, that confirms what I thought. I found out that binding tree is built during page composition and after that it seems to be unchangeable. I spent a lot of time with trying to add some data beans to the existing internal structure but nothing was successful. We have rewritten part of ZK framework ( also part of binding ) but this problem hasn't been solved yet. I have to find some other solution. Thanks a lot, again
Gaim