0

Cannot wire components within applied template

asked 2023-11-14 20:53:33 +0800

holos gravatar image holos
39 6

updated 2023-11-15 16:33:11 +0800

I applied a template within a zul

<apply templateURI="..." />

This template contains an element with the id="myId".

In my MVVM ViewModel class I try to access this component via

@Wire("#myId")
Component myId;

and

@AfterCompose
    public void afterCompose(@ContextParam(ContextType.VIEW) Component view) {
        Selectors.wireComponents(view, this, false);
    }

But myId always stays null.

Note: It works for each component on root level (container zul) but for no component within any template).

delete flag offensive retag edit

4 Answers

Sort by ยป oldest newest most voted
1

answered 2023-11-20 23:25:12 +0800

MDuchemin gravatar image MDuchemin
2480 1 6
ZK Team

Hey Holos,

Thanks for the extra info.

Regarding binding through a template URI: That should work, regardless of whether you are using a template inline, or a templateURI. The apply component will treat its descendants as part of the component tree, which makes them searchable.

This said, is there any IDSpace components in between your viewModel's host component and the target? (IDspace components are mostly window components, plus a few other containers, see the interface here)

IDspaces isolate parts of the component tree from each-other, and have to be navigated by adding the idspace holder as part of the component selector when wiring.

https://www.zkoss.org/wiki/ZKDeveloper'sReference/UIComposing/IDSpace

From a pure MVVM standpoint, this can be done without wiring components.

The tabbox has a few options that can be triggered by data-bindings.

The easiest one is that if you use a ListModel as the model for the tabbox, you can set the selected tab by updating the selection of the ListModelList. Assuming you pass the model used to create these tabs from the VM, you can just update the selection state on the ListModelList, and you don't even need to notify it.

another option is setSelectedIndex, if you are not using a model obtained from the main VM.

If you bind a value to the selectedIndex attribute of the tabbox, it can be notified to cause this API to trigger.

Alternatively, you can notify a value bound to the apply's attributes and cause the entire apply to be redrawn (if that's more in line with your workflow)

See this updated fiddle:

https://zkfiddle.org/sample/4bphu4/5-view-model-selection

it contains sample of everything above - wiring inside of the templateURI page - wiring inside of an idspace - using other MVVM-safe patterns to control the selected tab of the tabbox

link publish delete flag offensive edit

Comments

I switched implementation to your suggestion as <tabbox selectedIndex="@load(selectedIndex)"> This works.

holos ( 2023-11-21 20:07:44 +0800 )edit

Thanks a lot for your effort! I really appreciate your support and I really learned a lot :-)

holos ( 2023-11-21 20:14:53 +0800 )edit

good to hear! The MVVM pattern can be more challenging, because of its very decoupled nature. There are use cases where breaking the pattern makes sense, so these options exists in ZK's APIs, but I do recommend checking there is a MVVM-safe way to do a given operation.

MDuchemin ( 2023-11-21 22:09:06 +0800 )edit

btw, if you are passing a value to the template as an attribute on the <apply> tag, consider using either "@ref" or "@load". If you notify a variable bound through @ref on the apply, it doesn't drop and recreate the apply content. if you notify a @load binding on apply, it redraws all of the apply.

MDuchemin ( 2023-11-21 22:10:56 +0800 )edit
1

answered 2023-11-20 16:25:12 +0800

MDuchemin gravatar image MDuchemin
2480 1 6
ZK Team

Hey there,

If you are using wiring with shadow elements, one big thing that might be going wrong is timing.

See sample below which separates each phase as an example: https://zkfiddle.org/sample/4bphu4/2-view-model-selection

In order for Selectors.wireComponents to work, the component has to already exist in the component tree.

However, shadow elements recreate their content during the binding phase, which occurs AFTER the command / after compose phase.

As a result, when you call the Selectors.wireComponents during afterCompose, the shadow component itself exist (it's part of the composition phase), but it has not created its content yet (which it does during binding phase)

In addition, if you have already bound components and you trigger a notification which causes the shadow component parent to redraw, this will also nullifiy the wiring, because component retrieved from the first binding is no longer part of the page.

Instead, a new component is created by the shadow component, and you would need to rewire THAT one.

Here's a sequence you can try with the sample above:

  1. load page, click on "show component" <- this returns nothing, because afterCompose was execute before the textbox was initialized during binding phase.
  2. click on "rewire", click on "show component" <- this returns the current textbox, because wiring has happened AFTER the textbox was created.
  3. click on "update", click on "show component" <- this returns the OLD textbox (which is now nulled), instead of returning the new textbox created by the update to the apply's bound value
  4. click on "rewire", click on "show component" <- this return the new textbox (same idea as 2nd step)

With all of that said, what is your higher level requirement? (why do you need to wire a component in a VM?)

In the MVVM pattern, there might be different approach to do the type of operation which you are trying to achieve, which doesn't require direct component access. For example, using the shadow component apply, you can trigger the template to recreate itself with different parameters, if your goal is to perform updates.

And if your requirement absolutely require direct component access, there are ways to insert a smaller MVC patterned template inside of a larger MVVM page, but the specifics would depend on what you are trying to achieve.

link publish delete flag offensive edit

Comments

Our higher level requirement is the following:

We have a "tabbed" interface. If one clicks "save" all inputs on all tabs are checked. As soon as an error is detected, an error message box is shown. Here, we want to "select" the tab carrying the error using the "wired" tabbox.

holos ( 2023-11-20 18:38:48 +0800 )edit

But as I see from your fiddle, I can redo the wiring within a command handler (here: save). I will try this first.

holos ( 2023-11-20 18:41:59 +0800 )edit

Did not work. The difference might be, that I am using <apply templateURI="..." /> rather than just adding a component to the apply container.

holos ( 2023-11-20 18:59:56 +0800 )edit
0

answered 2023-11-15 09:06:37 +0800

hawk gravatar image hawk
3205 1 5
http://hawkphoenix.blogsp... ZK Team

updated 2023-11-15 09:07:01 +0800

@Wire wires with ID selector by default, see https://www.zkoss.org/wiki/ZKDeveloper%27sReference/MVC/Controller/WireComponents#WirewithIDselectorbyDefault

So you should write like this:

@Wire
Component myId;

or

@Wire("#myId")
Component myComponent;
link publish delete flag offensive edit

Comments

Hi hawk, thanks for the quick answer.

Unfortunately, this is not the solution. The problem is the template. I am afraid I have to use a selector according to section "Shadow Selectors". I already tried this - but to be honest: I don't get it. How would I have to do it in this case?

holos ( 2023-11-15 16:04:03 +0800 )edit
0

answered 2023-11-20 16:28:13 +0800

MDuchemin gravatar image MDuchemin
2480 1 6
ZK Team

FYI relevant to previous answer, the phase order in ZK is:

Composition: build the component tree <-- afterCompose happens here

[lots of things happen between these steps]

COMMAND: do a command
VALIDATE: do validate before a command or zk event
SAVE_BEFORE: do save bindings before executing a command
LOAD_BEFORE: do load bindings before executing a command
EXECUTE: execute a command <--- update happens here in my previous sample
SAVE_AFTER: do save bindings after executing a command
LOAD_AFTER: do load bindings after executing a command
SAVE_BINDING: do a save binding
LOAD_BINDING: do a load binding <--- shadow components recreate their content on Load binding, so the new Textbox is created here
link publish delete flag offensive edit
Your answer
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
1 follower

RSS

Stats

Asked: 2023-11-14 20:53:33 +0800

Seen: 13 times

Last updated: Nov 20

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