Wire Variables"

From Documentation
m
 
(12 intermediate revisions by 4 users not shown)
Line 1: Line 1:
 
{{ZKDevelopersReferencePageHeader}}
 
{{ZKDevelopersReferencePageHeader}}
  
=Wiring Sequence=
+
=Wire Variables=
 +
<javadoc>org.zkoss.zk.ui.select.SelectorComposer</javadoc> not only wires UI components, but also wires beans from implicit objects and registered variable resolvers.
 +
 
 +
 
 +
&nbsp;
 +
==Wire from Implicit Objects==
 +
Wiring from an implicit object is equivalent to calling <javadoc method="getImplicit(org.zkoss.zk.ui.Page, java.lang.String)">org.zkoss.zk.ui.Components</javadoc>, by the name specified on <code>@WireVariable</code>. If the name is absent and the field or method parameter is of type <javadoc type="interface">org.zkoss.zk.ui.Execution</javadoc>, <javadoc type="interface">org.zkoss.zk.ui.Page</javadoc>, <javadoc type="interface">org.zkoss.zk.ui.Desktop</javadoc>, <javadoc type="interface">org.zkoss.zk.ui.Session</javadoc>, or <javadoc type="interface">org.zkoss.zk.ui.WebApp</javadoc>, it still will be wired to the correct implicit object. However, in other cases, an exception will be thrown.
 +
 
 +
<source lang="java">
 +
public class FooComposer extends SelectorComposer<Window> {
 +
 +
@WireVariable
 +
private Page _page;
 +
 +
@WireVariable
 +
private Desktop _desktop;
 +
 +
@WireVariable
 +
private Session _sess;
 +
 +
@WireVariable
 +
private WebApp _wapp;
 +
 +
@WireVariable("desktopScope")
 +
private Map<String, Object> _desktopScope;
 +
 
 +
}
 +
</source>
 +
 
 +
&nbsp;
 +
 
 +
==Wire from Variable Resolver==
 +
There are two approaches to register a variable resolver: the <javadoc type="interface">org.zkoss.zk.ui.select.annotation.VariableResolver</javadoc> annotation or [[ZUML Reference/ZUML/Processing Instructions/variable-resolver|the variable-resolver directive]]. Here is the example of registering variable resolvers with annotations.
  
<javadoc>org.zkoss.zk.ui.util.GenericForwardComposer</javadoc> will wire members defined in the composer. Here is the sequence:
+
<source lang="java" highlight="1">
 +
@VariableResolver({foo1.MyResolver.class, foo2.AnotherResolver.class})
 +
public class FooComposer extends SelectorComposer<Gird> {
 +
....
 +
}
 +
</source>
  
#It searches any setters if its name matches any of the following
+
To have <javadoc>org.zkoss.zk.ui.select.SelectorComposer</javadoc> wire a variable, you have to annotate it with the <javadoc type="interface">org.zkoss.zk.ui.select.annotation.WireVariable</javadoc> annotation. For example,
##Any fellow with the same name (<javadoc method="getFellow(java.lang.String)">org.zkoss.zk.ui.Component</javadoc>)
 
##Any variable defined in <javadoc method="addVariableResolver(org.zkoss.xel.VariableResolver)">org.zkoss.zk.ui.Page</javadoc> with the same name
 
##Any variable defined in zscript with the same name
 
##[[ZUML Reference/EL Expressions/Implicit Objects|Any implicit object]] with the same name
 
#Then, it searches any field to see if it matches any fellow, any variable,... the same as the above.
 
  
=Wire Spring-managed Beans=
+
<source lang="java" highlight="3,5">
 +
@VariableResolver({foo1.MyResolver.class, foo2.AnotherResolver.class})
 +
public class FooComposer extends SelectorComposer<Gird> {
 +
    @WireVariable
 +
    Department department;
 +
    @WireVariable
 +
    public void setManagers(Collection<Manager> managers) {
 +
        //...
 +
    }
 +
}
 +
</source>
  
<javadoc>org.zkoss.zk.ui.util.GenericForwardComposer</javadoc> not only wires UI components, but also wires beans that can be found by the registered variable resolvers. Thus, if you'd like <javadoc>org.zkoss.zk.ui.util.GenericForwardComposer</javadoc> to wire the Spring-managed beans, you could register the Spring variable resolver, <javadoc>org.zkoss.zkplus.spring.DelegatingVariableResolver</javadoc>. Then, if a data member's name matches a Spring-managed bean, it will be wired automatically too. For example,
+
==Wire Spring-managed Beans==
 +
If you'd like <javadoc>org.zkoss.zk.ui.select.SelectorComposer</javadoc> to wire the Spring-managed beans, you can register the Spring variable resolver, <javadoc>org.zkoss.zkplus.spring.DelegatingVariableResolver</javadoc> with <code>@VariableResolver</code>. Then, you can annotate <code>@WireVariable</code> to wire a Spring-managed bean. It's wired according to its variable name as the bean's *id*. For example,
  
<source lang="java" high="2,6">
+
<source lang="java" highlight="1,3">
public class PasswordSetter extends GenericForwardComposer {
+
@VariableResolver(org.zkoss.zkplus.spring.DelegatingVariableResolver.class)
     private User user; //wired automatically if user is a spring-managed bean
+
public class PasswordSetter extends SelectorComposer<Window> {
 +
    @WireVariable
 +
     private User user;
 +
    @Wire
 
     private Textbox password; //wired automatically if there is a textbox named password
 
     private Textbox password; //wired automatically if there is a textbox named password
  
     public void doAfterComposer(Component comp) {
+
    @Listen("onClick=#submit")
         page.addVariableResolver(new org.zkoss.zkplus.spring.DelegatingVariableResolver());
+
     public void submit() {
            //variable resolver must be added before calling super.doAfterCompose
+
         user.setPassword(password.getValue());
        super.doAfterCompose(comp); //invoke autowiring
 
 
     }
 
     }
     public void onClick$submit() {
+
}
         user.setPassword(password.getValue());
+
</source>
 +
 
 +
<javadoc>org.zkoss.zkplus.spring.DelegatingVariableResolver</javadoc> is a variable resolver used to retrieve the Spring-managed bean, so the variable will be retrieved and instantiated by Spring.
 +
 
 +
Notice that the variables are wired before instantiating the component and its children, so you can use them in EL expressions. For example, assume we have a composer as follows.
 +
 
 +
<source lang="java" highlight="1,3">
 +
@VariableResolver(org.zkoss.zkplus.spring.DelegatingVariableResolver.class)
 +
public class UsersComposer extends SelectorComposer<Window> {
 +
    @WireVariable
 +
    private List<User> users;
 +
 
 +
     public ListModel<User> getUsers() {
 +
         return new ListModelList<User>(users);
 
     }
 
     }
 
}
 
}
 
</source>
 
</source>
  
As shown, we could register the variable resolver in <code>doAfterCompose</code> by use of
+
Then, you could reference to <code>getUsers()</code> in the ZUML document. For example,
<javadoc method="addVariableResolver(org.zkoss.xel.VariableResolver)">org.zkoss.zk.ui.Page</javadoc><ref>Instead of registering it manually, you could use [[ZUML Reference/ZUML/Processing Instructions/variable-resolver|the variable-resolver directive]] in the ZUML document.</ref>.
 
  
{{references}}
+
<source lang="xml">
 +
<window apply="UsersComposer">
 +
    <grid model="${$composer.users}">
 +
...
 +
</source>
 +
 
 +
where <code>$composer</code> is a built-in variable referring to the composer. For more information, please refer to [[ZK Developer's Reference/MVC/Controller/Composer|the Composer section]].
 +
 
 +
<blockquote>
 +
----
 +
<references/>
 +
</blockquote>
  
 
==Warning: Not a good idea to have Spring managing the composer==
 
==Warning: Not a good idea to have Spring managing the composer==
There is a tendency to make the composer as a Spring-managed bean. For example, assume we have a composer called <code>passwordSetter</code> and managed by Spring (i.e., annotated with Spring's <code>@Component</code>), then we might do as follows.
+
There is a tendency to make the composer a Spring-managed bean. For example, assume we have a composer called <code>passwordSetter</code> and managed by Spring, then we might do as follows.
  
 
<source lang="xml">
 
<source lang="xml">
Line 48: Line 116:
 
<source lang="java">
 
<source lang="java">
 
@Component
 
@Component
public class PasswordSetter extends GenericForwardComposer {
+
public class PasswordSetter extends SelectorComposer {
 
   @Autowired User user;
 
   @Autowired User user;
 
...
 
...
 
</source>
 
</source>
  
Unfortunately, this approach is error-prone. The reason is that none of Spring's scopes matches correctly with the lifecycle of the composers. For example, if the Session scope is used, it will cause errors when the user opens two browser windows to visit the same page. In this case, the same composer will be used to serve all desktops in the given session.
+
Unfortunately, this approach is error-prone. The reason is that none of Spring's scopes matches correctly with the lifecycle of the composers. For example, if the Session scope is used, it will cause errors when the user opens two browser windows to visit the same page. In this case, the same composer will be used to serve all desktops in the given session, and it is wrong.
  
The Prototype scope is a better choice since a new instance is instantiated for each request. However, it also implies another new instance will be instantiated if the Spring variable resolver is called to resolve the same name again in the later requests. It sounds unlikely, but it might be triggered implicitly and hard to debug. For example, it happens if some of your code evaluates an EL expression that references the composer's name, when an event received.
+
The Prototype scope is a better choice since a new instance is instantiated for each request. However, it also implies another new instance will be instantiated if the Spring variable resolver is called to resolve the same name again in the later requests. It is unlikely, but it might be triggered implicitly and hard to debug. For example, it happens if some of your code evaluates an EL expression that references the composer's name, when an event is received.
  
 
[http://www.zkoss.org/product/zkspring ZK Spring] is recommended if you want to use Spring intensively. It extends Spring to provide the scopes matching ZK lifecycle, such as the IdSpace and Component scopes. Please refer to [[ZK Spring Essentials]] for more detailed information.
 
[http://www.zkoss.org/product/zkspring ZK Spring] is recommended if you want to use Spring intensively. It extends Spring to provide the scopes matching ZK lifecycle, such as the IdSpace and Component scopes. Please refer to [[ZK Spring Essentials]] for more detailed information.
  
=Wire CDI-managed Beans=
+
==Wire other variables in GenericForwardComposer based composers==
The approach to work with CDI is similar to the approach for Spring, except the variable resolver for CDI is <javadoc>org.zkoss.zkplus.cdi.DelegatingVariableResolver</javadoc>.
+
Composers extending <javadoc>org.zkoss.zk.ui.util.GenericAutowireComposer</javadoc> such as <javadoc>org.zkoss.zk.ui.util.GenericForwardComposer</javadoc> will automatically wire variables based on convention during doAfterCompose(Component comp).
  
=zscript and Variable Resolver=
+
This method supports autowiring based on naming convention. You don't need to specify annotations explicitly, but it is error-prone if it is used improperly.
  
By default, variables defined in both zscript and variable resolvers will be checked<ref>It will be default to '''false''' in ZK 6 for better performance.</ref>. There might be some performance penalty if you have too much zscript code, or your variable resolver is too slow.
+
This wiring by convention will wire variables based on their name in composer, if a bean registered with the same name can be found in the following locations:
 +
* If enabled: ZScript variable of same name;
 +
* If enabled: Xel variable of same name
 +
* Attribute of component holding the composer declaration
 +
* Attribute of component's ancestor components
 +
* Attribute of component's page
 +
* Attribute of component's desktop
 +
* Attribute of component's session
 +
* Attribute of component's webapp
  
You could turn them off by passing false to the second and third argument of <javadoc method="GenericForwardComposer(char, boolean, boolean)">org.zkoss.zk.ui.util.GenericForwardComposer</javadoc>:
+
Searching in zscript and xel variables can be enabled with library properties:
  
<source lang="java">
+
[[ZK%20Configuration%20Reference/zk.xml/The%20Library%20Properties/org.zkoss.zk.ui.composer.autowire.zscript|enable zscript variable wiring]]
public class MyComposer extends GenericForwardComposer {
 
    public MyComposer() {
 
        super('$', false, false);
 
    }
 
}
 
</source>
 
  
Notice if you disable the wiring of EL variables (i.e., variables defined in a variable resolver), you can't wire the Spring-managed beans as described in the previous section.
+
[[ZK%20Configuration%20Reference/zk.xml/The%20Library%20Properties/org.zkoss.zk.ui.composer.autowire.xel|enable xel variable wiring]]
  
<blockquote>
+
=Wire CDI-managed Beans=
----
+
The approach to work with CDI is similar to the approach for Spring, except the variable resolver for CDI is <javadoc>org.zkoss.zkplus.cdi.DelegatingVariableResolver</javadoc>.
<references/>
 
</blockquote>
 
  
=ID Space=
+
=Wiring Sequence=
 
 
For components that are inside of another ID space, you could use <code>id1$id2$id3</code> to access it. More precisely, it will cause GenericForwardComposer to look for <code>id1</code> in the same ID space as the applied component, and then look for, if found and it is another ID space, <code>id2</code>, and so on. For example, you could find the textbox by <code>inner$input</code>.
 
  
<source lang="xml">
+
When extending from <javadoc>org.zkoss.zk.ui.select.SelectorComposer</javadoc>, the fields and methods with the proper annotations will be wired automatically. Here is the sequence of wiring:
<window apply="foo.MyComposer">
 
  <window id="inner">
 
        <textbox id="input"/>
 
...
 
</source>
 
  
Here is another example: suppose have two ZUL files:
+
*In <javadoc method="doBeforeCompose(org.zkoss.zk.ui.Page, org.zkoss.zk.ui.Component, org.zkoss.zk.ui.metainfo.ComponentInfo)">org.zkoss.zk.ui.util.ComposerExt</javadoc>, it wires variables to the fields and methods annotated with the <javadoc type="interface">org.zkoss.zk.ui.select.annotation.WireVariable</javadoc> annotation. Here is the sequence of how it looks for the variable:
 +
*# First, it will look for the variable resolver defined in the ZUML document (by use of <javadoc method="addVariableResolver(org.zkoss.xel.VariableResolver)">org.zkoss.zk.ui.Page</javadoc>).
 +
*# Second, it looks for the variable resolver annotated at the class with the <javadoc type="interface">org.zkoss.zk.ui.select.annotation.VariableResolver</javadoc> annotation.
 +
*# If none is found, it looks for [[ZUML Reference/EL Expressions/Implicit Objects|the implicit objects]], such as session and page.
  
{|
+
=Version History=
!width="350px"|'''Main file'''
 
!width="350px"|'''Included file - includeme.zul'''
 
|-
 
|
 
<source lang="xml">
 
<window id="mywindow" apply="MyComposer">
 
    <include id="i" src="includeme.zul" />
 
</window>
 
</source>
 
|
 
<source lang="xml">
 
  <textbox id="username" />
 
</source>
 
|}
 
 
 
To access the textbox "username" from "MyComposer", you could specify:
 
  
<source lang="java">
+
{| class='wikitable' | width="100%"
public class MyComposer extends GenericAutowireComposer {
 
    Textbox i$username;
 
    ...
 
}
 
</source>
 
 
 
=Version History=
 
{{LastUpdated}}
 
{| border='1px' | width="100%"
 
 
! Version !! Date !! Content
 
! Version !! Date !! Content
 
|-
 
|-
| &nbsp;
+
| 6.0.0
| &nbsp;
+
| February 2012
| &nbsp;
+
| @WireVariable was introduced.
 
|}
 
|}
  
 
{{ZKDevelopersReferencePageFooter}}
 
{{ZKDevelopersReferencePageFooter}}

Latest revision as of 06:47, 29 January 2024

Wire Variables

SelectorComposer not only wires UI components, but also wires beans from implicit objects and registered variable resolvers.


 

Wire from Implicit Objects

Wiring from an implicit object is equivalent to calling Components.getImplicit(Page, String), by the name specified on @WireVariable. If the name is absent and the field or method parameter is of type Execution, Page, Desktop, Session, or WebApp, it still will be wired to the correct implicit object. However, in other cases, an exception will be thrown.

public class FooComposer extends SelectorComposer<Window> {
	
	@WireVariable
	private Page _page;
	
	@WireVariable
	private Desktop _desktop;
	
	@WireVariable
	private Session _sess;
	
	@WireVariable
	private WebApp _wapp;
	
	@WireVariable("desktopScope")
	private Map<String, Object> _desktopScope;

}

 

Wire from Variable Resolver

There are two approaches to register a variable resolver: the VariableResolver annotation or the variable-resolver directive. Here is the example of registering variable resolvers with annotations.

@VariableResolver({foo1.MyResolver.class, foo2.AnotherResolver.class})
public class FooComposer extends SelectorComposer<Gird> {
....
}

To have SelectorComposer wire a variable, you have to annotate it with the WireVariable annotation. For example,

@VariableResolver({foo1.MyResolver.class, foo2.AnotherResolver.class})
public class FooComposer extends SelectorComposer<Gird> {
    @WireVariable
    Department department;
    @WireVariable
    public void setManagers(Collection<Manager> managers) {
        //...
    }
}

Wire Spring-managed Beans

If you'd like SelectorComposer to wire the Spring-managed beans, you can register the Spring variable resolver, DelegatingVariableResolver with @VariableResolver. Then, you can annotate @WireVariable to wire a Spring-managed bean. It's wired according to its variable name as the bean's *id*. For example,

@VariableResolver(org.zkoss.zkplus.spring.DelegatingVariableResolver.class)
public class PasswordSetter extends SelectorComposer<Window> {
    @WireVariable
    private User user;
    @Wire
    private Textbox password; //wired automatically if there is a textbox named password

    @Listen("onClick=#submit")
    public void submit() {
        user.setPassword(password.getValue());
    }
}

DelegatingVariableResolver is a variable resolver used to retrieve the Spring-managed bean, so the variable will be retrieved and instantiated by Spring.

Notice that the variables are wired before instantiating the component and its children, so you can use them in EL expressions. For example, assume we have a composer as follows.

@VariableResolver(org.zkoss.zkplus.spring.DelegatingVariableResolver.class)
public class UsersComposer extends SelectorComposer<Window> {
    @WireVariable
    private List<User> users;

    public ListModel<User> getUsers() {
        return new ListModelList<User>(users);
    }
}

Then, you could reference to getUsers() in the ZUML document. For example,

<window apply="UsersComposer">
    <grid model="${$composer.users}">
...

where $composer is a built-in variable referring to the composer. For more information, please refer to the Composer section.


Warning: Not a good idea to have Spring managing the composer

There is a tendency to make the composer a Spring-managed bean. For example, assume we have a composer called passwordSetter and managed by Spring, then we might do as follows.

<?variable-resolver class="org.zkoss.zkplus.spring.DelegatingVariableResolver"?>
<window apply="${passwordSetter}">
...
@Component
public class PasswordSetter extends SelectorComposer {
   @Autowired User user;
...

Unfortunately, this approach is error-prone. The reason is that none of Spring's scopes matches correctly with the lifecycle of the composers. For example, if the Session scope is used, it will cause errors when the user opens two browser windows to visit the same page. In this case, the same composer will be used to serve all desktops in the given session, and it is wrong.

The Prototype scope is a better choice since a new instance is instantiated for each request. However, it also implies another new instance will be instantiated if the Spring variable resolver is called to resolve the same name again in the later requests. It is unlikely, but it might be triggered implicitly and hard to debug. For example, it happens if some of your code evaluates an EL expression that references the composer's name, when an event is received.

ZK Spring is recommended if you want to use Spring intensively. It extends Spring to provide the scopes matching ZK lifecycle, such as the IdSpace and Component scopes. Please refer to ZK Spring Essentials for more detailed information.

Wire other variables in GenericForwardComposer based composers

Composers extending GenericAutowireComposer such as GenericForwardComposer will automatically wire variables based on convention during doAfterCompose(Component comp).

This method supports autowiring based on naming convention. You don't need to specify annotations explicitly, but it is error-prone if it is used improperly.

This wiring by convention will wire variables based on their name in composer, if a bean registered with the same name can be found in the following locations:

  • If enabled: ZScript variable of same name;
  • If enabled: Xel variable of same name
  • Attribute of component holding the composer declaration
  • Attribute of component's ancestor components
  • Attribute of component's page
  • Attribute of component's desktop
  • Attribute of component's session
  • Attribute of component's webapp

Searching in zscript and xel variables can be enabled with library properties:

enable zscript variable wiring

enable xel variable wiring

Wire CDI-managed Beans

The approach to work with CDI is similar to the approach for Spring, except the variable resolver for CDI is DelegatingVariableResolver.

Wiring Sequence

When extending from SelectorComposer, the fields and methods with the proper annotations will be wired automatically. Here is the sequence of wiring:

Version History

Version Date Content
6.0.0 February 2012 @WireVariable was introduced.



Last Update : 2024/01/29

Copyright © Potix Corporation. This article is licensed under GNU Free Documentation License.