0

Declarative MVC binding

asked 2010-08-18 13:46:41 +0800

red3 gravatar image red3
84 2

updated 2010-08-18 13:49:01 +0800

I am setting up an AJAX UI framework & conventions for my team and have decided on ZK (of course!)

I read the article about MVC Made Easy (http://www.zkoss.org/smalltalks/mvc3/)
and found the example posted here: http://www.pichelhofer.at/ZKDemo/ (SimpleComposer6 example.)

I think these are up to date examples, but ZK is moving so fast there may now be a better way!
Please let me know if I'm missing something!

From these examples, this is what I have discovered:
- Field to bean binding (auto-wiring) is achieved by the apply attribute being a GenericForwardComposer (or subclass of it)
- Implicit Event handler method binding is achieved through the Initializer, which by default uses this convention (event$componentId - e.g. onClick$btnSubmit)
- Component binding is also achieved by allowing the programmer to declare component by matching id in the Composer class (I prefer to call it a Controller than a Composer.)

This is what I have so far:

MyFormController.java:

public class MyFormController extends GenericForwardComposer {

	//These objects are handles on their UI counterparts
	private Window main;
	private Panel pnlForm;
	private Panel pnlConfirm;

	//The alias for the controller (used as the reference for binding)
	private String alias = "controllerBean;
	
	//The binder class that does the auto-wiring 'magic'
	private AnnotateDataBinder binder;
	
	//The data container object - its attributes are bound to the UI components
	//using value="@{controllerBean.domainobject.property}" syntax in the view
	private DataObject dataBean = new DataObject();
	
	private Service service = new Service();

	@Override
	public void doAfterCompose(Component win) throws Exception {
		super.doAfterCompose(win);
		win.setAttribute(this.alias, this, false); // make models visible for the databinder
	}

	public void onCreate$main(ForwardEvent event) {
		// get the databinder for later extra load and save
		// note: binder is not ready in doAfterCompose, so do it here
		binder = (AnnotateDataBinder) event.getTarget().getAttribute("binder",
				false);
	}

	public DataObject getDataBean() {
		return dataBean;
	}

	public void setSubscription(DataObject dataBean) {
		this.dataBean = dataBean;
	}

	public void onClick$btnSubmit(Event event) {
		//This will test constraints and save all bound values of BOUND fields
		//If a constraint is violated, an exception will be raised immediately and handled by the framework
		binder.saveComponent(main);
		
		...validate any unbound components...
		
		 response = service.doSomething(dataObject);

	}
	
}

In the view:

<?page id="myPage" title="My Autowired Page"?>
<?init class="org.zkoss.zkplus.databind.AnnotateDataBinderInit" root="main"?>

<zk>
		<window id="main"
			apply="com.tcs.gla.ui.registration.RegistrationFormController"
			width="450px">
...

This works well, but when creating a framework for others to use, I'd like to make some further improvements.

Issues I have:
- Bindings in the ZUL view are done like this value="@{composerRef.databean.propertyname}"
The composerRef identifier is defined in the composer! To make things neater, I would like to be able to define it in the view.

Things I would like to achieve:
- Create a base class that encapsulates the ability to configure the binding controller in the form.
- Allow the programmer to declare the references they will use in the ZUL file

This is how I thought I might achieve this:
- Create a base BoundFormController class which allows me to set the controller reference, root window reference, etc.
- Create an BoundFormInitializer class which allows me to set the values of these references and to define the sub-class of "BoundFormController" specific to my form.
- Declare the configuration of the controller using the <?init... directive in my Form

Here's my enhanced declarative binding ZUL file:

?page id="myPage" title="My Autowired Page"?>
<?init class="org.zkoss.zkplus.databind.AnnotateDataBinderInit" root="myForm"?>
<?init class="com.tcs.gla.ui.BoundFormControllerInit" root="myForm" alias="myController" controller="com.tcs.gla.ui.registration.MyFormController"?>

<zk>
		<window id="myForm"
			apply="${myController}"
                        onCreate="doFormCreate()"
			width="450px">
...

This way I can specify in the ZUL page the reference to use in my component bindings:
value="@{myController.user.name}"

To me this is more intuitive.

I have got this all working except for one thing. The EL expression in the form apply="${myController}" evaluates to nothing! It should be a reference to the for controller object. I have tried to set it as an attribute on the window and the page and use ${pageScope.myController}, but it still evaluates to nothing. I think that it's a lifecycle. I have also tried setting the apply property on the window programatically, but I am not sure how to do that.

So without a reference when I apply the controller to the window, the bindings are not made.
Perhaps I should forget using the apply attribute and do it in code?
Is this feasible? How, when and where do I programatically bind the form to the controller bean?

delete flag offensive retag edit

3 Replies

Sort by ยป oldest newest

answered 2010-08-18 17:08:50 +0800

robertpic71 gravatar image robertpic71
1275 1

First, the SimpleComposer6 is almost up to date. I've done only small changes:

public class SimpleComposer7 extends GenericForwardComposer {

	private AnnotateDataBinder binder;
...
	@Override
	public void doAfterCompose(Component win) throws Exception {
             super.doAfterCompose(win);
             // create an own databinder instance --> no <?init class...> line!!
            binder = new AnnotateDataBinder(win);
            binder.bindBean("controller", this);  // bind this controller to the databinder
            binder.loadAll();
           ...
 

Instead of the zul line <?init Annotation... and catch the onCreate$win-event, i
create my own databinder instance inside the afterCompose.
In the real world code i work with spring and apply looks like apply="${mainController}".

>> The composerRef identifier is defined in the composer! To make things neater, I would like to be able to define it in the view.
It is also possible @{myForm$composer.bean.attribute}". But this is long path. I use this as shortcut: controller (= composer)
and model (model/service-layer).

>> Things I would like to achieve:
>> - Allow the programmer to declare the references they will use in the ZUL file
I post this feature request for something like this. I would be nice, that the ZK Studio knows the binding for autocompletion and syntaxcheck.

>> - Create a base class that encapsulates the ability to configure the binding controller in the form.
I think Springresourcename is not that bad. There is also an "unofficial" spring annotation @controller to bind zul and controller.

>> I have got this all working except for one thing. The EL expression in the form apply="${myController}"
For ZK 5 you could register your controller as attribute:
win.setAttribute("controller", this, Component.COMPONENT_SCOPE);

Check this thread for backgroundinfos.

/Robert

link publish delete flag offensive edit

answered 2010-08-18 18:34:06 +0800

red3 gravatar image red3
84 2

Thanks for the reply, Robert.

We are not using Spring in the framework (yet) and so for now I think I will live with what I have.
I think that I could do what I need with Spring.

Definitely would like to see code-completion for bindings in ZK Studio. That would be neat.

I'm going to try your binding code in my Base class and see how it goes.

It seems ZK needs to polish this up and publish the 'preferred' clean way of doing auto-wired MVC for normal programmers. I think they are nearly there, but it should be spelled out clearly, straight-forward and intuitive for newcomers. I've spent about a week working through this and it's only thanks to your example code, Robert, that I was able to put everything together.

Thanks again!

link publish delete flag offensive edit

answered 2010-08-18 21:50:34 +0800

robertpic71 gravatar image robertpic71
1275 1

>> ZK needs to polish this up and publish the 'preferred' clean way of doing auto-wired MVC
This has some historical reasons. The composer was built-in later. First databinding uses
the extend UI-Components as datasource.

However, there is still room for improvment. Meanwhile i use a strict nameschema.

my.zul
MyController, Binding: Controller
MyModel, Binding; Model

Of course, for simple zul's with shared controllers/models - it's always 1:1:1.

Please note: MVC with databinding is only one way. Some other people works to Render for lists or make a 100% Java-UI like Swing or GWT. Some other use ZK Spring Webflow. I'm not an official ZK "best-practice-speaker".

/Robert

link publish delete flag offensive edit
Your reply
Please start posting your answer anonymously - your answer will be saved within the current session and published after you log in or create a new account. Please try to give a substantial answer, for discussions, please use comments and please do remember to vote (after you log in)!

[hide preview]

Question tools

Follow

RSS

Stats

Asked: 2010-08-18 13:46:41 +0800

Seen: 903 times

Last updated: Aug 18 '10

Support Options
  • Email Support
  • Training
  • Consulting
  • Outsourcing
Learn More