0

using Path.getComponent within doAfterCompose

asked 2009-06-17 17:16:31 +0800

simonmassey gravatar image simonmassey
90 1

I am using GenericAutowireComposer and MVC pattens. I have a div of links that I want to use to show and hide other divs in the desktop:

			<div sclass="content" apply="${sectionComposer}">
				<vbox>
					<category value="Transactional Data" />
					<item id="policies" label="Policies">
						<custom-attributes show="homecontainer" hide="productscontainer"/>
					</item>
					<category value="Reference Data" />
					<item id="products" label="Products">
						<custom-attributes show="productscontainer" hide="homecontainer"/>
					</item>
				</vbox>
			</div>

I am using <custom-attributes/> to name the IDs of the <div/> components that are elsewhere on the page. My problem comes within my composer when I want to resolve the components that are other divs not inside my Composer that I want to show and hide. If within the controller doAfterCompose I try to use div.getFellow() or Path.getComponent(...) as the desktop is not properly setup I get null when trying to resolve the components who's IDs are named by the custom attributes.

Something that almost works is to use the doAfterCompose to register an onCreate event to run after the desktop is assembled:

	@Override
	public void doAfterCompose(final org.zkoss.zk.ui.Component comp) throws Exception {
		super.doAfterCompose(comp);
		sections = Collections.unmodifiableList(Arrays.asList(new Toolbarbutton[]{policies, products}));
		for( final Toolbarbutton item : sections) { // why Toolbarbutton is class of <item/> in zul?
			final String show = (String) item.getAttribute("show");
			final String hide = (String) item.getAttribute("hide");
			Component showComponent = Path.getComponent("//root/"+show);
			Component hideComponent = Path.getComponent("//root/"+hide);
			// defer the setup of the panels until the whole UI has been composed - do it on the onCreate Event phase
			item.addEventListener("onCreate", new EventListener(){
				public void onEvent(Event event) throws Exception {
					Component showComponent = Path.getComponent("//root/"+show);
					Component hideComponent = Path.getComponent("//root/"+hide);
					item.setAttribute("showComponent", showComponent, Component.COMPONENT_SCOPE);
					item.setAttribute("hideComponent", hideComponent, Component.COMPONENT_SCOPE);
					showComponent = (Component)item.getAttribute("showComponent", Component.COMPONENT_SCOPE);
					hideComponent = (Component)item.getAttribute("hideComponent", Component.COMPONENT_SCOPE);
				}});
		}
	}
	|

The catch with that is that the onCreate that I have bound dynamically upon compose is not spotted by ZK. I have to actually put a empty onCreate event into the ZUL to have ZK run those registered event listeners at a point when Path.getComponent finally finds the other components.

What is the correct approach to this? Is it the wrong approach to try to have Composer "see out beyond" what it is composing? Would it be better for me to use an <?init?> declaration that is Page wide do to this initialization?

thx

Simon

delete flag offensive retag edit

6 Replies

Sort by ยป oldest newest

answered 2009-06-18 01:43:03 +0800

henrichen gravatar image henrichen
3869 2
ZK Team

updated 2009-06-18 01:46:36 +0800

The doAfterCompose() method of a composer is executed when all children of the applied component is created and composed. And the traversal of the component for loading is "deep first".

root - a1 - a2
     - b1 - b2
     - c1 - c2

Assume the composer is applied on a1.

The traversal sequence is then root - a1 - a2 - a1 (doAfterCompose of Composer) - b1 - b2 - b1 - c1 - c2 - c1 - root. Trying to look for b1 and c1 for example by Path.getComponent(...) in the Composer you will not locate them because they are not created and attached on to the Page yet.

I think <?init?> might be the proper way to do such "binding" in your case. With the same example the traversal sequence:

(doInit() of Initiator) - root - a1 - a2 - a1 (doAfterCompose called) - b1 - b2 - b1 - c1 - c2 - c1 - root - (doAfterCompose of Initiator or InitatorExt) - ... - onCrate()

As for why you need to manually add an empty onCreate="" in zul to make it work. This is kind of unexpected, I will check it.

link publish delete flag offensive edit

answered 2009-06-18 08:46:56 +0800

simbo1905 gravatar image simbo1905
31 2

Thanks Henri,

I have added demo code to a bug report which demonstrates the unexpected behaviour here https://sourceforge.net/tracker/?func=detail&aid=2808240&group_id=152762&atid=785191

Simon

link publish delete flag offensive edit

answered 2009-06-18 11:40:59 +0800

simonmassey gravatar image simonmassey
90 1

Okay here is a solution (could become a SmallTalk) to the design issue that I was running where I should have used <?init?> to link up my Composers. This source works when added to the demo code project at the bug report URL above.

The demo behaviour is to be able to toggle visibility of things on the page so that only one item is showing. The page looks it is normal tabs but its generalized so you could be clicking upon any components and be showing and hiding any other hiding any other components.

<?init class="org.zkforge.zktodo2.SectionsInit"?>
<zk>
	<toolbar>
		<toolbarbutton label="Policies" apply="org.zkforge.zktodo2.SectionsToggleComposer">
			<custom-attributes show="policiescontainer"/>
		</toolbarbutton>
		<space/>
		<toolbarbutton label="Products" apply="org.zkforge.zktodo2.SectionsToggleComposer">
			<custom-attributes show="productscontainer"/>
		</toolbarbutton>
	</toolbar>
	<div>
		<div apply="org.zkforge.zktodo2.SectionsPanelComposer">
			<custom-attributes panel-name="productscontainer"/>
			productscontainer
		</div>
		<div apply="org.zkforge.zktodo2.SectionsPanelComposer">
			<custom-attributes panel-name="policiescontainer"/>
			policiescontainer
		</div>
	</div>
</zk>

So that ZUL uses an init class and two composer classes. Notice the <custom-attributes/> tags so that the page designer can "configure" the behaviour.

Here is the init class which acts as the "clearing house" for all of the composer events. It is actually playing the role of Controller in terms of the MVC pattern; not the Composer objects which only bind components to the init controller.

package org.zkforge.zktodo2;

import java.util.Map;
import java.util.TreeMap;

import org.zkoss.zk.ui.Component;
import org.zkoss.zk.ui.Page;
import org.zkoss.zk.ui.util.Initiator;

public class SectionsInit implements Initiator, SectionsListener {

	// only one panel should be showing so hide the rest
	public void doAfterCompose(Page page) throws Exception {
		boolean first = true;
		for( Map.Entry<String,Component> e: this.pathToSectionsMap.entrySet() ){
			Component component = e.getValue();
			component.setVisible(first);
			first = false;
		}
		return;
	}

	public boolean doCatch(Throwable arg0) throws Exception {
		return false;
	}

	public void doFinally() throws Exception {
	}

	/**
	 * Exposes this object to components in the page
	 */
	public void doInit(Page page, Object[] arg1) throws Exception {
		page.setAttribute(SectionsListener.class.getCanonicalName(), this);
	}

	private Map<String,Component> pathToSectionsMap = new TreeMap<String,Component>();
	
	/**
	 * Register a section
	 */
	public void registerSection(Component component) {
		String panelName = (String)component.getAttribute("panel-name");
		pathToSectionsMap.put(panelName, component);
	}

	public void toggleSection(String panel) {
		for( Map.Entry<String,Component> e: this.pathToSectionsMap.entrySet() ){
			String p = e.getKey();
			Component component = e.getValue();
			boolean show = (p.equals(panel))?true:false; 
			component.setVisible(show);
		}
		return;
	}

}

Here is its contract interface for components that want to be involved:

package org.zkforge.zktodo2;

import org.zkoss.zk.ui.Component;

public interface SectionsListener  {
	public abstract void registerSection(Component component);
	
	public abstract void toggleSection( String show );
}

Here is the composer that enlists a component with the init class:

package org.zkforge.zktodo2;

import org.zkoss.zk.ui.Component;
import org.zkoss.zk.ui.util.Composer;

public class SectionsPanelComposer implements Composer {
	public void doAfterCompose(Component comp) throws Exception {
		SectionsListener sectionsListener = (SectionsListener)comp.getAttribute(SectionsListener.class.getCanonicalName(), Component.PAGE_SCOPE);
		sectionsListener.registerSection(comp);
		return;
	}
	
}

and here is the composer that sets up the events to be directed to the init class:

package org.zkforge.zktodo2;

import org.zkoss.zk.ui.Component;
import org.zkoss.zk.ui.event.Event;
import org.zkoss.zk.ui.event.EventListener;
import org.zkoss.zk.ui.util.Composer;

public class SectionsToggleComposer implements Composer {

	public void doAfterCompose(Component component) throws Exception {
		final SectionsListener sectionsListener = (SectionsListener) component
				.getAttribute(SectionsListener.class.getCanonicalName(),
						Component.PAGE_SCOPE);
		final String show = (String)component.getAttribute("show", Component.COMPONENT_SCOPE);
		component.addEventListener("onClick", new EventListener() {
			public void onEvent(Event event) throws Exception {
				sectionsListener.toggleSection(show);
			}
		});
	}

}

So with this design it is the init class which is actually the Controller not the Composer classes. Rather than the init class having to be told what components are involved components are registered by using "apply" and the configuration is via the XML custom-attributes tag.

As I say the actual screen in the zul looks like it could have been done with tabs. That simple UI design is only to demonstrate an init controller implementation of the MVC pattern that could have far more complex UI behavours.

Simon

link publish delete flag offensive edit

answered 2009-06-19 00:59:49 +0800

henrichen gravatar image henrichen
3869 2
ZK Team

Simon,

> "Okay here is a solution (could become a SmallTalk) ..."

Nice work! I am looking forward to your small talk ;-)

Smalltalk is now wiki based and it shall help the community a lot (Please don't let this hide in the "forum sea")

Help:Writing_Small_Talks

link publish delete flag offensive edit

answered 2009-06-19 08:00:18 +0800

simbo1905 gravatar image simbo1905
31 2

I have check that fulfill works well with this approach just as it works well with the tabs example in the DevGuide. I have to put the fulfill on an element above the panels that are composed else the 'apply' on the panel runs when the panel is not properly setup with its custom attributes:

<?init class="org.zkforge.zktodo2.SectionsInit"?>
<zk>
	<toolbar>
		<toolbarbutton label="Products" apply="org.zkforge.zktodo2.SectionsToggleComposer">
			<custom-attributes show="productscontainer"/>
		</toolbarbutton>
		<space/>
		<toolbarbutton id="policyToggle" label="Policies" apply="org.zkforge.zktodo2.SectionsToggleComposer">
			<custom-attributes show="policiescontainer"/>
		</toolbarbutton>
	</toolbar>
	<div>
		<div apply="org.zkforge.zktodo2.SectionsPanelComposer">
			<custom-attributes panel-name="productscontainer"/>
			productscontainer
		</div>
		<div fulfill="policyToggle.onClick">
			<div apply="org.zkforge.zktodo2.SectionsPanelComposer">
				<custom-attributes panel-name="policiescontainer"/>
				policiescontainer
			</div>
		</div>
	</div>
</zk>

In that code notice the <div fulfill="policyToggle.onClick"> that wraps the second panel references the ID of the toolbar button that is performing the toggle and just exists to prevent the panel being created until the user has click the toolbarbutton. I ran this it in a debugger and the components in the second panel are not constructed until I click the button. Then the panel composer for the second panel then works well with this lazy instantiation and registers the panel with the Initiator composer. The toggle logic work to toggle between the panels. Neat.

link publish delete flag offensive edit

answered 2009-06-19 08:28:19 +0800

simbo1905 gravatar image simbo1905
31 2

I have committed the code to the zktodo2 demo project.
The demo zul is under svn here. Documentation on how to build it is here.

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: 2009-06-17 17:16:31 +0800

Seen: 1,607 times

Last updated: Jun 19 '09

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