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

rickcr
704 7

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?

[EDITED]

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 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

Sort by » oldest newest most voted

cor3000
4596 2 7

Hi,

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).

Regards Robert

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);

( 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...

( 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

( 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?

( 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

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

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">


Now Tab 1 uses this include..

<include mode="instant" src="@init('/pages/myListBox.zul')" pageType="TIER_ITEMS" />


Another tab 2 has

<include mode="instant" src="@init('/pages/myListBox.zul')" pageType="DIFFERNT_PAGE" />


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.

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

//VM
@Init
}


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

 <listitem


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

cor3000
4596 2 7

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