answered
2022-09-26 17:08:28 +0800
MDuchemin 2480 ● 1 ● 6
Hey there, regarding your two problems:
1 - the idspace question:
An ID must be unique inside of an Idspace.
An ID space is "all of the descendants of a space owner. If one of the descendant is a space owner themselves, then their own descendants belong to their own ID Space, not to the first ID space"
https://www.zkoss.org/wiki/ZKDeveloper'sReference/UIComposing/IDSpace
In this case, the HtmlMacroComponent class that you need to extend is automatically an IDspace.
This means that you can have components holding IDs inside of your MacroComponents, and they will not collide with other instances of the same components located in other instances of the same macroComponent in the same page.
Consider a macro component "myTextInput" defined by the following zul:
<div>
<textbox id="textInput"/>
</div>
If you instantiate it multiple time in a page, such as:
<zk>
<myTextInput value="..."/>
<myTextInput value="..."/>
<zk>
Essentially, you are writing:
<zk>
<div> <!-- the macroComponent root element which is just an extended DIV-->
<div>
<textbox id="textInput" value="..."/>
</div>
</div>
<div> <!-- the macroComponent root element which is just an extended DIV-->
<div>
<textbox id="textInput" value="..."/>
</div>
</div>
</zk>
You can see that as a result, you have multiple components holding the same ID in the same page, HOWEVER this is not a problem, because your macroComponent root elements acts as IDspace owners, and they isolates the IDs located inside, making this a valid page.
<zk>
<div> <!-- the macroComponent root element which is just an extended DIV-->
<!-- ID space 1 start -->
<div>
<textbox id="textInput" value="..."/>
</div>
<!-- ID space 1 ends -->
</div>
<div> <!-- the macroComponent root element which is just an extended DIV-->
<!-- ID space 2 start -->
<div>
<textbox id="textInput" value="..."/>
</div>
<!-- ID space 2 ends -->
</div>
</zk>
Important note: See how the ID space is located INSIDE of the space owner, but the space owner itself is not in that space.
That means that if you have multiple of your macroComponent inside the same parent space, you need to give them different IDs, otherwise you break the rule of no duplicated IDs per ID space. For example, this is not OK:
<zk>
<myTextInput id="test" value="..."/>
<myTextInput id="test" value="..."/>
<zk>
In your code, since you declared an ID during component constructor:
setId("test");
This cause every single instances of your component to have the same ID.
With all that said, the main question is why does your component needs an ID? :)
In general, in MVVM you are not supposed to lookup a component by ID or act on a component directly.
In this pattern, the component is agnostic of the viewModel. The communication between them should only happen through value binding and events (which are passed as commands).
For example, instead of reading vm.myValue from inside of the component class, you would bind it in zul.
<mycomponent compvalue="@load(vm.myValue)"/>
As a result, the binder will call myComponent#setCompValue everytime a notifyChange on vm.myValue gets triggered. (and also during first binding when the component is created)
See here a sample of how the individual component and the VM communicate:
https://github.com/patchypanel/zkbooks/blob/master/developersreference/developersreference/src/main/java/org/zkoss/reference/developer/mvvm/advance/EditableLabelXml.java https://github.com/patchypanel/zkbooks/blob/5a365271f12d500f340dc8ad3c01ca3561d47585/developersreference/developersreference/src/main/webapp/advance/custom-component.zul
(it is using xml for binder declaration, but the logic would be the same with annotations: https://github.com/patchypanel/zkbooks/blob/5a365271f12d500f340dc8ad3c01ca3561d47585/developersreference/developersreference/src/main/resources/metainfo/zk/lang-addon.xml)
2 - Regarding the command / listen problem
There is a pattern shift here between the outer page using MVC and the MacroComponent content using MVC.
Since the MacroComponent is designed to use MVC pattern, with event listeners and direct internal component access through wiring, the logic works differently inside and outside of the macro component.
In this case, you can consider that the MacroComponent itself actually acts as a MVC composer (all of the features @Listen, @Wire, etc are substantially similar).
This is not an issue in the normal case, since the macroComponent isolates its content from the rest of the page and acts as the intermediate during MVVM operations.
In practice, that means the MacroComponent receiving a value change from the binder, then doing MVC calls to update its own descendants, etc.
In the case of listening to events on components generated by templates, that's something you can do in MVC as well :)
https://www.zkoss.org/wiki/ZKDeveloper'sReference/EventHandling/EventForwarding
Event forwarding is one way to do it.
On the created component itself, you can define a forwarding event name and target.
For example, in this case you would forward from the header button to grid onHeaderButtonClick event
Then in the MacroComponent, you can listen to @Listen("onHeaderButtonClick=#myGrid") (assuming you gave the grid an ID).
From there, the event you are receiving is a forwardEvent, which you can do event.getOrigin() to retrieve the original click event and the original target button.
Important side-note:
If you want to do MVVM all the way down (not doing a logic shift from inside and outside of the macroComponent), you can consider ZK EE zuti dependency's shadow elements.
For example the <apply> shadow elements is a functional upgrade to the <include> component, which lets you target specific templates of your choice and pass attributes / commands.
http://books.zkoss.org/zk-mvvm-book/9.5/syntax/apply.html
It is in the EE package, but it might also be matching what you are trying to do.