0

[HELP!!] Composite Component and data binding: Strange behaviour

asked 2012-02-24 12:41:48 +0800

dan75 gravatar image dan75
45

First of all, excuse my bad English!
I followed ZK 5 instructions about how to create a custom component using Composite Component.

If I use my custom component directly into a zul file, the data binding is performed.
If I create an instance of my custom component into a java class and try to append it to its parent, data binding doesn't work
Am I forgetting anything? Why this difference behavior?
I enclose a very simple and reproducible example!

Composite component zul file "id_div.zul"

<?page title="new page title" contentType="text/html;charset=UTF-8"?>
<?init class="org.zkoss.zkplus.databind.AnnotateDataBinderInit" arg0="./cmbPersons"?>
<zk>
	<combobox id="cmbPersons" model="@<composer.arg.allPersons>">
		<comboitem self="@{each='person'}" label="@{person.name}" value="@{person.id}"/>
	</combobox>
</zk>

Composite Component java class IdDiv.java

package zk.ui;

import zk.model.Person;
import java.util.*;
import org.zkoss.zk.ui.*;
import org.zkoss.zul.Div;

public class IdDiv extends Div implements IdSpace 
{
	private List<Person> listPersons = new ArrayList<Person>(5);
	
	public IdDiv()
	{
		super();
		
		Executions.createComponents("id_div.zul", this, null);
		Components.wireVariables(this, this, '$', false, false);
		Components.addForwards(this, this, '$');
		
		listPersons.add(new Person(1, "Jack Aubrey"));
		listPersons.add(new Person(2, "Stephen Maturin"));
		listPersons.add(new Person(2, "Pullings Tom"));
	}

	public List<Person> getAllPersons()
	{
		return listPersons;
	}
}

Bean Person

package zk.model;

public class Person 
{
	private Integer id = 0;
	private String name = null;
	
	public Person(){
		super();
	}
	
	public Person(Integer id, String name){
		setId(id);
		setName(name);
	}

	public Integer getId() {
		return id;
	}

	public void setId(Integer id) {
		this.id = id;
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}
}

Main zul "main.zul"

<?page title="MainController" contentType="text/html;charset=UTF-8"?>
<?init class="org.zkoss.zkplus.databind.AnnotateDataBinderInit"?>
<?component name="iddiv" extends="div" class="zk.ui.IdDiv"?>

<zk>
<window id="pbWin" title="MainController" border="normal" apply="zk.MainController">

	<button id="addCC" label="Add new custom CC"/>
	
	<vlayout id="mainLayout">
		<!-- This one works correctly! Databinding ok -->
		<iddiv />
		
		<!-- Other dynamic instances appended here via addCC event
		     don't show anything. Databind KO -->
	</vlayout>
	
</window>
</zk>

MainController.java the responsable to add dynamically new Instance of my IdDiv t into a vlayout

package zk;

import org.zkoss.zk.ui.Component;
import org.zkoss.zk.ui.util.GenericForwardComposer;
import org.zkoss.zul.Vlayout;
import zk.ui.IdDiv;

public class MainController extends GenericForwardComposer
{
	private Vlayout mainLayout;
	
	public void doAfterCompose(Component comp) throws Exception 
	{
		super.doAfterCompose(comp);
	}
	
	public void onClick$addCC()
	{
		// A new instance of my Composite Component that will be appended at main layout
		IdDiv child = new IdDiv();
		
		// My Composite Component is displayed, but without no data is shown into the combo box. Why??
		mainLayout.appendChild(child);
	}
}

delete flag offensive retag edit

8 Replies

Sort by ยป oldest newest

answered 2012-02-28 09:33:40 +0800

dan75 gravatar image dan75
45

Hi Matz I don't know how to thank you!!
You are giving me a very big help!
Today I'll try your suggestions.

However, yes I have multiple instances of my component. I need this feature because I have to produce something like the old Struts Tiles that I can add or remove.
I tried first the Composite Component pattern, because I would like use this "tiles" like a normal component (for egs <label/> <textbox/> etc.)
This implies that all instance have to control their components and comunicate with the parent composer (MainComposer) through an interfaces (using MVC) to set and retrieve data inserted.
Something like:

class MainController
{
   onClick$add()
   {
      MyTile tile = new MyTile();
      tile.setParent(this);
      tile.setModel(getTileModel());
      tile.setCallback(this);

      listTiles.add(tile);
   }

   onClick$doSave()
   {
      for(MyTile item:listTiles)
      {
          if(item.validate())
          {
              TileDataModel model =item.getModel();
          }
      }
   }
}

link publish delete flag offensive edit

answered 2012-02-27 19:27:25 +0800

Matze2 gravatar image Matze2
773 7

You have multiple parallel instances? Then you need unique ids (note that the argument is called arg0, not root):

<?init class="org.zkoss.zkplus.databind.AnnotateDataBinderInit" arg0="./win${arg.seq}"?>
<window id="win${arg.seq}" ...

arg.seq is just an integer which you increment at each createComponents() call and pass it in the "arg" Map of createComponents.
Remains the lots of occurrences of "id$composer", they would need to be dynamic, too...

To avoid this you can define a top-level bind variable in doAfterCompose():

	public void doAfterCompose(Component comp) throws Exception {
		super.doAfterCompose(comp);
		comp.setAttribute("abc", this);

then you can use
@{abc.<property>}

instead.

link publish delete flag offensive edit

answered 2012-02-27 17:34:45 +0800

dan75 gravatar image dan75
45

Hi Mat, thanks for the help

I already tried to assign the id to the window, ok works fine but just with one instance.
I have necessity to add a lot of this instances into my "tileLayout", due to obtain a collection of it!

MainController allows the user to "add" (not replace) all instances he want and when requested, should be able to communicate with them to get the data inserted.

link publish delete flag offensive edit

answered 2012-02-27 16:18:32 +0800

Matze2 gravatar image Matze2
773 7

From the first quick view, I see two differences to my implementations:

1. I use the window id in AnnotateDataBinderInit, not the div element, something like this

<?init class="org.zkoss.zkplus.databind.AnnotateDataBinderInit" root="./hwWin"?>
<zk>
	<window id="hwWin" title="new page title" border="none" >

Of course all your need to change all your "id$composer" patterns then.
But I don't know if this makes any difference.

2. I detach the created components before call createComponents with this method

public static void detachComponents(Component parent) {
	if (parent == null || parent.getChildren() == null) {
		return;
	}
	parent.getChildren().clear();
}
in your case, you would call
detachComponents(tileLayout);
before createComponents() call.

link publish delete flag offensive edit

answered 2012-02-27 14:25:20 +0800

dan75 gravatar image dan75
45

Hi Matze2... I tried to follow your suggest (thanks for all).
I discarded Composite Component solution because for me it doesn't work when used in dynamic mode (custom component created at runtime via Java).

With your solution (if i understood well your suggest) now the behavior works as I aspect , but now i don't know how to retrieve data from my components.
Unlike your suggest, I can't assign the id to the window (sub-zul), I had to refer to an internal div, because when I invoke

 Executions.createComponents("hw_comp.zul", tileLayout, null); 
work just for the first time, at the second time I obtain an error about unique ID.
A strange thing is: if i use <include> tag, there isn't problem with ID, just when i use Executions!

I post my new solution!

index.zul

<?page title="Auto Generated index.zul"?>
<?init class="org.zkoss.zkplus.databind.AnnotateDataBinderInit"?>

<window title="Hello World!! c" border="normal" width="100%" height="100%" apply="zk.MainController">
	<borderlayout>
		<north>
			<vlayout>
				<label value="You are using: ${desktop.webApp.version}"/>
				<button id="addTile" label="Add Tile"/>
			</vlayout>
		</north>
		<center>
			<vlayout id="tileLayout">
				<include src="hw_comp.zul"/>
			</vlayout>
		</center>
	</borderlayout>
</window>

MainController.java

package zk;

import org.zkoss.zk.ui.Executions;
import org.zkoss.zk.ui.util.GenericForwardComposer;
import org.zkoss.zul.Vlayout;

public class MainController extends GenericForwardComposer 
{
	private static final long serialVersionUID = 9080698052377671570L;
	
	private Vlayout tileLayout;

	public void onClick$addTile()
	{
		if(tileLayout != null)
		{
			Executions.createComponents("hw_comp.zul", tileLayout, null);
		}
	}
}

hw_comp.zul

<?page title="new page title" contentType="text/html;charset=UTF-8"?>
<?init class="org.zkoss.zkplus.databind.AnnotateDataBinderInit" root="./hwDiv"?>
<zk>
	<window  title="new page title" border="none" >
		<div id="hwDiv" apply="zk.HWCompComposer">
			<zscript><![CDATA[
				List listP = new ArrayList(5);
				listP.add(new zk.model.Person(1, "Metallica"));
				listP.add(new zk.model.Person(2, "Motorhead"));
				listP.add(new zk.model.Person(3, "Black Label Society"));
				
		    ]]></zscript>
	
			<hlayout>
				<vlayout>
					Combo by java
					<combobox id="cmb01" model="@{hwDiv$composer.listI}">
						<comboitem self="@{each='pItem'}" label="@{pItem.name}" value="@{pItem.id}"/>
					</combobox>
				</vlayout>
				<vlayout>
					Combo by Z-Script
					<combobox id="cmb02" model="@{listP}">
						<comboitem self="@{each='pItem'}" label="@{pItem.name}" value="@{pItem.id}"/>
					</combobox>
				</vlayout>
			</hlayout>
		</div>
	</window>
</zk>

HWCompComposer.java

package zk;

import java.util.ArrayList;
import java.util.List;

import org.zkoss.zk.ui.Component;
import org.zkoss.zk.ui.Page;
import org.zkoss.zk.ui.metainfo.ComponentInfo;
import org.zkoss.zk.ui.util.GenericForwardComposer;

import zk.model.Person;

public class HWCompComposer extends GenericForwardComposer 
{
	private static final long serialVersionUID = -9029997856631485278L;

	private List<Person> listI = null;
	
	public List<Person> getListI()
	{
		return listI;
	}

	/* (non-Javadoc)
	 * @see org.zkoss.zk.ui.util.GenericComposer#doBeforeCompose(org.zkoss.zk.ui.Page, org.zkoss.zk.ui.Component, org.zkoss.zk.ui.metainfo.ComponentInfo)
	 */
	@Override
	public ComponentInfo doBeforeCompose(Page page, Component parent,
			ComponentInfo compInfo) 
	{
		listI = new ArrayList<Person>(5);
		listI.add(new Person(1, "Jack Aubrey"));
		listI.add(new Person(2, "Stephen Maturin"));
		listI.add(new Person(2, "Pullings Tom"));
		
		return super.doBeforeCompose(page, parent, compInfo);
	}
}

link publish delete flag offensive edit

answered 2012-02-25 23:21:58 +0800

Matze2 gravatar image Matze2
773 7

There are still some differences:
1. In my case window is the top-level component of the sub-zul file. In your case it is parent component placed in the main zul file.
2. arg0 specifies the window id in my case, which is the IdSpace component. In your case it specifies only one of the components of the ZUL file.

In other words, you "leave the main path" with your custom component solution. It could be that you have to walk that track alone then.

link publish delete flag offensive edit

answered 2012-02-25 19:33:14 +0800

dan75 gravatar image dan75
45

I never saw that binding syntax:
"@<composer.arg.allPersons>"

Ops!!! I do not know how it could be happened, but in my source code I have the right version:

"@<composer.arg.allPersons>"

However! your solution couldn't work in my case! Combobox is a child of my CC, this one is the composer not the combo!
My Composite Component has it's own AnnotationDataBinder ("arg0")..

Your Windows solution it's the same to my CC which one extend a Div and implements IdSpace. Main trouble is the dynamic instances of it created at runtime. If I use my CC inline to zul, annotation databinder work fine, but not if I create in my java class and append it to a parent!

link publish delete flag offensive edit

answered 2012-02-25 07:31:26 +0800

Matze2 gravatar image Matze2
773 7

I never saw that binding syntax:

"@<composer.arg.allPersons>"

Shouldn't this be something like
"@{cmbPersons$composer.allPersons}"
insteead?

By the way : for "sub-zul-pages" I usually use <window> component without border to get an IdSpace.
No special component needed.

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: 2012-02-24 12:41:48 +0800

Seen: 361 times

Last updated: Feb 28 '12

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