Wire Variables"
m |
(ZK 6) |
||
Line 1: | Line 1: | ||
{{ZKDevelopersReferencePageHeader}} | {{ZKDevelopersReferencePageHeader}} | ||
− | =Wiring | + | =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. | ||
+ | |||
+ | |||
+ | | ||
+ | ==Wire from Implicit Objects== | ||
+ | Wiring from 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 SelectComposer<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> | ||
+ | |||
+ | | ||
− | <javadoc>org.zkoss.zk.ui. | + | ==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. | ||
− | + | <source lang="java" high="1"> | |
− | + | @VariableResolver({foo1.MyResolver.class, foo2.AnotherResolver.class}) | |
− | + | public class FooComposer extends SelectComposer<Gird> { | |
− | + | .... | |
− | + | } | |
− | + | </source> | |
− | = | + | To have <javadoc>org.zkoss.zk.ui.select.SelectorComposer</javadoc> to wire a variable, you have to annotate it with the <javadoc type="interface">org.zkoss.zk.ui.select.annotation.WireVariable</javadoc> annotation. For example, |
− | < | + | <source lang="java" high="3,5"> |
+ | @VariableResolver({foo1.MyResolver.class, foo2.AnotherResolver.class}) | ||
+ | public class FooComposer extends SelectComposer<Gird> { | ||
+ | @WireVariable | ||
+ | Department department; | ||
+ | @WireVariable | ||
+ | public void setManagers(Collection<Manager> managers) { | ||
+ | //... | ||
+ | } | ||
+ | } | ||
+ | </source> | ||
− | <source lang="java" high=" | + | ==Wire Spring-managed Beans== |
− | public class PasswordSetter extends | + | If you'd like <javadoc>org.zkoss.zk.ui.select.SelectorComposer</javadoc> to wire the Spring-managed beans, you could register the Spring variable resolver, <javadoc>org.zkoss.zkplus.spring.DelegatingVariableResolver</javadoc> with <code>@VariableResolver</code>. Then, you could annotate <code>@WireVariable</code> for wiring a Spring managed bean. For example, |
− | private User user; | + | |
+ | <source lang="java" high="1,3"> | ||
+ | @VariableResolver(org.zkoss.zkplus.spring.DelegatingVariableResolver) | ||
+ | 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 | + | @Listen("onClick=#submit") |
− | + | public void submit() { | |
− | + | user.setPassword(password.getValue()); | |
− | |||
} | } | ||
− | public | + | } |
− | + | </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" high="1,3"> | ||
+ | @VariableResolver(org.zkoss.zkplus.spring.DelegatingVariableResolver) | ||
+ | public class UsersComposer extends SelectorComposer<Window> { | ||
+ | @WireVariable | ||
+ | private List<User> users; | ||
+ | |||
+ | public ListModel<User> getUsers() { | ||
+ | return new ListModelList<User>(users); | ||
} | } | ||
} | } | ||
</source> | </source> | ||
− | + | Then, you could reference to <code>getUsers()</code> in the ZUML document. For example, | |
− | < | + | |
+ | <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> | <blockquote> | ||
Line 51: | Line 116: | ||
<source lang="java"> | <source lang="java"> | ||
@Component | @Component | ||
− | public class PasswordSetter extends | + | public class PasswordSetter extends SelectorComposer { |
@Autowired User user; | @Autowired User user; | ||
... | ... | ||
Line 65: | Line 130: | ||
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>. | 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>. | ||
− | = | + | =Wiring Sequence= |
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | + | 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: | |
− | < | + | *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 how it looks for the variable: |
− | + | *# First, it will look for the variable resolver defined in the ZUML document first (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. | |
− | |||
− | = | ||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | ... | ||
− | </ | ||
− | |||
− | Here is | ||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | </ | ||
− | < | ||
− | |||
− | |||
− | |||
− | |||
− | | | ||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
=Version History= | =Version History= | ||
Line 129: | Line 144: | ||
! Version !! Date !! Content | ! Version !! Date !! Content | ||
|- | |- | ||
− | | | + | | 6.0.0 |
− | | | + | | February 2012 |
− | | | + | | @WireVariable was introduced. |
|} | |} | ||
{{ZKDevelopersReferencePageFooter}} | {{ZKDevelopersReferencePageFooter}} |
Revision as of 10:50, 7 February 2012
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 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 SelectComposer<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 SelectComposer<Gird> {
....
}
To have SelectorComposer to 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 SelectComposer<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 could register the Spring variable resolver, DelegatingVariableResolver with @VariableResolver
. Then, you could annotate @WireVariable
for wiring a Spring managed bean. For example,
@VariableResolver(org.zkoss.zkplus.spring.DelegatingVariableResolver)
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)
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 as 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 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 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:
- In ComposerExt.doBeforeCompose(Page, Component, ComponentInfo), it wires variables to the fields and methods annotated with the WireVariable annotation. Here is the sequence how it looks for the variable:
- First, it will look for the variable resolver defined in the ZUML document first (by use of Page.addVariableResolver(VariableResolver)).
- Second, it looks for the variable resolver annotated at the class with the VariableResolver annotation.
- If none is found, it looks for the implicit objects, such as session and page.
Version History
Version | Date | Content |
---|---|---|
6.0.0 | February 2012 | @WireVariable was introduced. |