How to reuse same zul and backing ViewModel - more than one instance in scope [closed]

asked 2013-09-18 18:05:00 +0800

rickcr gravatar image rickcr
704 7

updated 2013-09-19 05:20:56 +0800

I'm trying to not repeat myself (DRY.) I have a paginated listbox that I'd like to reuse as an include on two (or more) different zuls, but both zuls could be in memory at the same time.

I like to back my components, including my listbox with a ViewModel (I've explained elsewhere the benefits of this.)

The issue I'm having though is in communication when the listbox viewModel needs to send out an even such as "row selected." Obviously I only want the "selectedRow" picked up by the OUTER view model. The page that has this included listbox zul on it. However, since it's a global event and the listbox and its viewmodel could be in scope on other pages... this event could end up picked up in the wrong VMs.

What's the best practice to avoid this?

So for example, let's say you have a "Manufacturer" screen a "Customer" screen... both backed by a ViewModel. Both screens use an "Automobiles list box" with a backing AutomobilesViewModel"

When the "selectedAutomobile" is targeted, you only want the appropriate VM (Manufacturer or Customer VM) to be notified... but how do you accomplish this? Do you need to pass around some kind of id from the AutomobilesVM when the global command is sent out and then ignore it in a VM if it doesn't match some id you care about?


Not to get too long winded, but let me further illustrate a problem. You have a listbox that you want to reuse. However the "selectedRow" you only want firing on the row on the outer ViewModel backing the page that the include of the listbox is used on. I can't figure out tho make this happen... eg

The include file has..

 <listbox id="itemsList" model="@load(vm.tierItems)"
         selectedItem="@bind(vm.selectedTierItem)" ..>

In the above the setter for selectedTierTiem needs to reside on the ViewModel backing the listbox include. I'd like it to "trickle up" instead to the enclosing ViewModel. Since I can't seem to do this, I have to have this UGLY kind of "if the include is on X page logic" send this global command... eg

//in my listbox view model

 public void setSelectedTierItem(TierItem selectedTierItem) {
    this.selectedTierItem = selectedTierItem;

           if (pageType.equals("TIERS")) {
                    Map<String, Object> args = new HashMap<String,Object>();
        args.put("selectedTierItem", selectedTierItem);
        BindUtils.postGlobalCommand(null, null, "tierItemSelectedForFooBarViewModel", args);
    } else {
                  //if other page type the include is used on

Do you see the issue I'm running into? It's not that easy to reuse a listbox on different views. I have to pass to 1) pass to the listbox view model what outer page I'm used on and 2) I have to different switches used within this Listbox view model to decide who to notify

It would be much nicer if somehow ZK would look for the outer VM somehow to handle the selected item, and there might be a way, I just don't know the syntax?

delete flag offensive retag edit

The question has been closed for the following reason "the question is answered, right answer was accepted" by rickcr
close date 2013-09-23 14:44:26

4 Answers

Sort by ยป oldest newest most voted

answered 2013-09-19 10:44:00 +0800

cor3000 gravatar image cor3000
5262 2 7
ZK Team

updated 2013-09-19 10:47:20 +0800


for both scenarios there are simple solutions.

For the select event you can use event forwarding http://books.zkoss.org/wiki/ZKDeveloper'sReference/EventHandling/EventForwarding

and for the context menu problem you can refer to the documentation... if 2 context menus have the same Id it will find the first one http://www.zkoss.org/javadoc/latest/zk/org/zkoss/zul/impl/XulElement.html#setContext(java.lang.String)

I created an example on zk fiddle that shows flexible solutions for both problems.

The example shows, how to separate both the reusable include from the pages including them. Now the automobile list does not need to know who uses it, it just requires an argument (the contextmenuid), so you can use different ids outside the include. the revers idea is used for the select event... it is simply forwarded as a specific event "onCarSelect" the page reusing the include, can decide how to handle it (and not the include).

I hope that answers your questions...

Regards Robert

link publish delete flag offensive edit


if you want to use global commands, it is possible to use different named queues like <window apply="org.zkoss.bind.BindComposer" viewModel="@id('vm') @init('foo.MyViewModel')" binder="@init(queueName='myqueue')"> and BindUtils.postGlobalCommand("myqueue", EventQueues.DESKTOP, "cmdX", args);

cor3000 ( 2013-09-19 10:59:47 +0800 )edit

Wow. Thanks so much! Mind you without your zk fiddle example I think I still would have been lost. Those docs you posted don't illustrate things as you have in your fiddle example. Because they only allow so few characters for a comment I'll posting one follow up question...

rickcr ( 2013-09-19 16:47:34 +0800 )edit

.. things are awesome (thanks to you) the only issue I seem to be having ( hopefully it's just a "duh" moment) but I can't seem to get the context menu to show up on a row when I'm using it on a listitem that is backed by a model. illustrating: http://zkfiddle.org/sample/bdue6a/6-reusable-include-p

rickcr ( 2013-09-19 16:55:19 +0800 )edit

I'm also curious though.. where in the docs anywhere does it describe what events are possible to forward on a listbox? where you have
listbox forward="onSelect=..." what other events are possible to forward and where do I find them? The Listbox API didn't seem to show an "onSelect" event?

rickcr ( 2013-09-20 21:40:23 +0800 )edit

see component reference http://books.zkoss.org/wiki/ZKComponentReference/Data/Listbox#Supported_Events + inherited from XulElement. in general avoid putting too much logic in setters... there should be an event in most cases

cor3000 ( 2013-09-20 22:44:35 +0800 )edit

answered 2013-09-19 05:50:10 +0800

rickcr gravatar image rickcr
704 7

Sigh. here's another thing frustrating. I want to have a different right click menu for the list box depending on the page the include is used on. What I just noticed is the first context menu is the one uses on ALL the page that have the include.

myListBox.zul has..

<listbox ....>
    <template name="model" var="item">
        <listitem onClick="@command('editItem')" context="itemGridPopUpMenu">

Now Tab 1 uses this include..

<include mode="instant" src="@init('/pages/myListBox.zul')" pageType="TIER_ITEMS" />
 <menupopup id="itemGridPopUpMenu">
     <menuitem label="FooBar" onClick="@command('fooBar')" />

Another tab 2 has

<include mode="instant" src="@init('/pages/myListBox.zul')" pageType="DIFFERNT_PAGE" />
 <menupopup id="itemGridPopUpMenu">
     <menuitem label="SomethingElse" onClick="@command('somethingElse')" />

If you access the first tab then access the second tab, the second tab included listbox has the "FooBar" right click context menu, NOT somethingElse.

It's actually becoming easier I think to repeat myself and copy and paste the same list box code with pagination in the VM rather than try to reuse this listbox VM.

link publish delete flag offensive edit

answered 2013-09-19 17:34:49 +0800

rickcr gravatar image rickcr
704 7

Here's how I got it to work, maybe not the best way? http://zkfiddle.org/sample/bdue6a/7-reusable-include-part2

I had to pass in the contextMenuItem as part of the init of the VM for the list.

    //list zul
    @init('net.learntechnology.AutomobilesVM', contextMenuId=contextMenuId)">

    public void init(@BindingParam("contextMenuId") String contextMenuId) {
         this.contextMenuId = contextMenuId;

I then used the vm's instance for the context in the listbox..


Couldn't have done this without your help Robert. thanks again

link publish delete flag offensive edit

answered 2013-09-23 04:34:48 +0800

cor3000 gravatar image cor3000
5262 2 7
ZK Team

updated 2013-09-23 04:36:16 +0800

I was able to simplify your example a bit more using reference binding for the contextMenuId-parameter - getting rid of the unpleasant attribute on AutomobileListVM.

updated files are: AutomobileListVM.java, automobileList.zul, customerSite.zul, manufacturerSite.zul

link publish delete flag offensive edit

Question tools

1 follower



Asked: 2013-09-18 18:05:00 +0800

Seen: 98 times

Last updated: Sep 23 '13

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