The DataBinder that reads ZUML annotations to create binding info.
You have two ways to annotate ZK components. For ZK data binding, you can use @{...} annotaion expression or <a:bind> annotation expression.
To use <a:bind> annotation expression, in the ZUML page you must declare the XML namespace,xmlns:a="http://www.zkoss.org/2005/zk/annotation"? first. Then declare <a:bind> before component to make the annotation.
We suggest annotating directly on the component property with intuitive @{...} expression because it is more readable.(我们建议直接在组件属性中使用注解指令,因为这样可读性更强)
For example, the following annotation associates the attribute "value" of the component "textbox" to the bean's value "person.address.city".
@{...} way:
<textbox value="@{person.address.city}"/>
The @{...} pattern tells the ZUML parser that this is for annotation.
<a:bind> way:
<a:bind value="person.address.city"/> <textbox/>
You can put more metainfo inside the @{...} or <a:bind> so this DataBinder knows what to do.?The complete format is like this:
@{...} way:
<componentX attrY="@{bean's value,[tag='expression']...}"/>
<a:bind> way:
<a:bind attrY="bean's value;[tag:expression]..."/>
<componentX/>
This associates the componentX's attribute attrY
to the bean's value. The bean's value is something in the form of
beanid.field1.field2... You can either call?DataBinder.bindBean(java.lang.String, java.lang.Object)
?to
bind the beanid to a real bean object or you can neglect it and this
DataBinder would try to find it from the variables map via?Page.getZScriptVariable(java.lang.String)
?thenComponent.getVariable(java.lang.String, boolean)
?method.
That is, all those variables defined in zscript are also accessible by
this DataBinder. Note that you can choose either two formats of
annotations as your will and you can even hybrid them together though it
is not generally a good practice.
The tag='expression' or tag:expression ?is a generic form to bind more metainfo to the attrY of the componentX.?The currently supported tags includes "load-when", "save-when", "access", "converter", "load-after"(since 3.6.1), and "save-after"(since 3.6.1).
-
load-when
. You can specify the
events concerned when to load the attribute of the component from the
bean. Multiple definition is allowed and would be called one by one.
Note that binding is executed BEFORE other event listeners you defined.
(If you want the loading to be executed AFTER your event listener, use
load-after). For example, the following code snip tells DataBinder that
the attribute "value" of Label "fullName" will load from
"person.fullName" when the Textbox "firstName" or "lastName" fire
"onChange" event.
The @{...} way that specify directly on the Component's attribute:
???注意第三个组件示例中load-when发生的时机:compId.onEventNameXXXX
<textbox id="firstname" value="@{person.firstName}"/> <textbox id="lastname" value="@{person.lastName}"/> <label id="fullname" value="@{person.fullName, load-when='firstname.onChange,lastname.onChange'}"/>
Or the <a:bind> way that declare in front of the Component:
<a:bind value="person.firstName"/> <textbox id="firstname"/> <a:bind value="person.lastName"/> <textbox id="lastname"/> <a:bind value="person.fullName; load-when:firstname.onChange; load-when:lastname.onChange"/> <label id="fullname"/>
- (Since 3.6.1)?load-after . Similar to load-when. You can specify the events concerned when to load the attribute of the component from the bean. The only difference is that the loading is done?AFTER ?other event listeners listening to the same event are processed; while in load-when the loading is done?BEFORE ?other event listeners listening to the same event are processed.
-
save-when
. You can specify the
events concerned when to save the attribute of the component into the
bean. Since ZK version 3.0.0, you can specify multiple events in
save-when tag (i.e. before ZK 3.0.0, you can specify only one event).
The events specified, if fired, will trigger this DataBinder to save the
attribute of the component into the specified bean. Note that binding
is executed BEFORE other event listeners you defined. (If you want the
saving to be executed AFTER your event listener, use save-after). For
example, the following code snip tells DataBinder that the property
"value" of Textbox "firstName" will save into "person.firstName" when
the Textbox itself fire "onChange" event.
The @{...} way that specify directly on the Component's attribute:
<textbox id="firstName" value="@{person.firstName, save-when='self.onChange'}"/>
Or the <a:bind> way that declare in front of the Component:
<a:bind value="person.firstName; save-when:self.onChange"/> <textbox id="firstName"/>
However, you don't generally specify the save-when tag. If you don't specify it, the default events are used depends on the natural characteristic of the component's property as defined in lang-addon.xml. For example, the save-when of Label.value is default to "none" while that of Textbox.value is default to "self.onChange". That is, the following example is the same as the above one.
The @{...} way that specify directly on the Component's attribute:
<textbox id="firstName" value="@{person.firstName}"/>
Or the <a:bind> way that declare in front of the Component:
<a:bind value="person.firstName"/> <textbox id="firstName"/>
On the other hand, you might not specify the save-when tag nor you want the default events to be used. Then you can specify a "none" keyword or simply leave empty to indicate such cases.
??以下配置情况采用默认事件触发
<textbox id="firstName" value="@{person.firstName, save-when='none'}"/>
or<textbox id="firstName" value="@{person.firstName, save-when=''}"/>
or<a:bind value="person.firstName; save-when:none;"/> <textbox id="firstName"/>
or<a:bind value="person.firstName; save-when: ;"/> <textbox id="firstName"/>
- (Since 3.6.1) save-after. Similar to save-when. You can specify the events concerned when to save the attribute of the component to the bean. The only difference is that the saving is doneAFTER ?other event listeners listening to the same event are processed; while in save-when the saving is done?BEFORE ?other event listeners listening to the same event are processed.
-
access
. You can set the
access mode of the attrY of the componentX to be "both"(load/save),
"load"(load Only), "save"(save Only), or "none"(neither).
?save允许修改bean值,load仅访问属性值,both具有save/load二者功能?
Multiple
definition is NOT allowed and the later defined would override the
previous defined one. The access mode would affects the behavior of the
DataBinder's loadXxx and saveXxx methods. The?
DataBinder.loadAll()
?andDataBinder.loadComponent(org.zkoss.zk.ui.Component)
?would load only those attributes with "both" or "load" access mode. The?DataBinder.saveAll()
?andDataBinder.saveComponent(org.zkoss.zk.ui.Component)
?would save only those attributes with "both" or "save" access mode. If you don't specify it, the default access mode depends on the natural characteristic of the component's attribute as defined in lang-addon.xml. For example, Label.value is default to "load" access mode while Textbox.value is default to "both" access mode. For example, the following code snips tells DataBinder that Textbox "firstName" would allowing doing save into bean only not the other way.The @{...} way that specify directly on the Component's attribute:
<textbox id="firstName" value="@{person.firstName, access='save'}"/>
Or the <a:bind> way that declare in front of the Component:
<a:bind value="person.firstName;access:save;"/> <textbox id="firstName"/>
- converter. ?可 以指定实现TypeConverter接口的converter类名字,它将负责组件属性和bean属性之间的互相转换.converter不允许配置多 个,如果定义多个那么后一个将覆盖前一个。大多说情况下你无需指定它,因为DataBinder支持大多说通常使用的类型,然后如果你指定 TypeConverter类名,那么DataBinder实例化这个转换器,替代默认转换器转换
(Since 3.1)如果你指定了其他的非特定的标签,那么他们将被放入map参数中,作为组件属性(key=bindingArgs)存储在组件作用域内,例如?<label id="mylabel" value="@{person.name, answer='yes'}" style="@{person.mystyle, question='no'}">
?然后 mylabel.getAttribute("bindingArgs") 将返回{answer=yes, question=no}两个记录, (自从 3.1)我们支持"distinct" 的概念,用于具有model属性的组件(即Grid, Listbox, Comobobox, 等等.).你可以像下面那样指定该属性告诉 Data Binder 多条记录中或许有相同的对象
<grid model="@{persons, distinct=false}" ...>
...
</grid>
or
<a:bind model="persons; distinct:false"/>
<grid ...>
...
</grid>
The default value for distinct is "true". However, if you specify distinct=false, the DataBinder will scan the whole ListModel to find out all items with the specified objects (thus worse performance if a big ListModel).
?
自从 3.6.2,如果你指定非特定标签,那么他们将被放入map参数中,并存储在以Xxx_bindingArgs命名的组件作用域内容, 其中Xxx表示注解组件属性的名字
<label id="mylabel" value="@{person.name, answer='yes'}" style="@{person.mystyle, question='no'}">
然后 mylabel.getAttribute("value_bindingArgs") 返回{answer=yes} , mylabel.getAttribute("style_bindingArgs") 返回另外一个map{question=no} entry.
?
自从3.0.0,DataBinder 支持保存前验证阶段,注:DataBinder "save"由save-when或者save-after指定事件触发时执行的行为。 在保存之前,首先触发注解绑定组件的onBindingSave事件,然后在触发onBindingValidate事件 ,在实际保存组件属性内容到bean属性之前触发 ,因此应用开发者可以在保存之前处理输入值的验证, 在下面的例子中,当user点击savebtn按钮结束以后,onBindingSave发送到firtName和lastName文本框,然后 onBindingValidate事件发送到savebtn按钮.应用开发者可以注册适当的事件处理程序做他们想做的事情
<textbox id="firstName" value="@{person.firstName, save-when="savebtn.onClick"}" onBindingSave="..."/> <textbox id="lastName" value="@{person.lastName, save-when="savebtn.onClick"}" onBindingSave="..."/> <button id="savebtn" label="save" onBindingValidate="..."/>
注意:textbox原有的约束机制让然起作用,该DataBinder验证阶段仅是一个附加特性,它可以用于所有组件的所有属性,
?
?
注释
注释提供了关于一个组件的数据,这些数据并不属于组件本身。它们在所注释组件的操作上并没有直 接的影响。而是,它们在运行时被检测(examine),主要由工具或管理者(a tool or a manager)使用。注释的内容和意义完全取决于开发人员使用的工具或管理者。例如,一个数据绑定(data-binding)管理者可以检测注释,以 知道组件值要被存储的数据源。
注释ZUML页面
注释可被用于ZUML页面中组件和属性的声明。有两种注释方式:标准的方式和简单的方式(the classic way and the simple way)。选择娜种看你的喜好。若喜欢的话可以在同一页面内混合使用它们。
注释组件声明的标准方式
注释要放在你想注释的元素声明之前:
?
?
?
<window xmlns:a="http://www.zkoss.org/2005/zk/annotation"> <vbox> <a:author name="John Magic" date="3/17/2006"/> <listbox> </listbox> ...
?
annotation为http://www.zkoss.org/2005/zk/annotation 空间内的一个元素。元素的名字和属性可以是依赖于你所使用工具的一切。你可以使用几个注释为相同的组件声明注释:
?
?
?
<a:author name="John Magic"/> <a:editor name="Mary White" date="4/11/2006"/> <listbox/>
?
author和editor为注释的名称,而name和date为属性名称。换言之,注释由名称和属性映射组成。
若两个注释有相同的名称,则它们会被合并为一个注释。例如,
?
?
?
<a:define var1="auto"/> <a:define var2="123"/> <listbox/>
?
等价于
?
?
?
<a:define var1="auto" var2="123"/> <listbox/>
?
[注]: 注释不支持EL表达式。
注释属性(Property)声明的标准方式
为注释一个属性声明,你可以将注释放置于属性声明之前,如下所示。
?
?
?
<listitem a:bind="datasource='author',name='name'" value="${author.name}"/>
?
或者,你可以使用attribute
元素,然后简单的注释属性声明,类似于为组件声明注释。换言之,上面的注释等价于下面的注释:
?
?
?
<listitem> <a:bind datasource="author" name="name"/> <attribute name="value">${author.name}</attribute> </listitem>
?
注:若忽略了属性名称,则名称为value
。例如,
?
?
?
<listitem a:bind="value='selected'" value=""/>
?
等价于
?
?
?
<listitem a:bind="selected" value=""/>
?
注释属性声明的简单方式
除了如上描述的使用特定XMl命名空间进行注释,有一种简单的注释属性的方法:为要注释的属性使用注释表达式指定一个值,如下所示。
?
?
?
<listitem label="@{bind(datasource='author',selected)}"/>
?
注释表达式的格式为@{
?annot-name
?(
?attr-name1
?=
?attr-value1,attr-name2=attr-value2
?)}
。换言之,若属性值为一个注释表达式,则会被认为是为相应属性的注释,而不是其值。在上面的例子中,名为bind
的注释为label
属性注释。因此,其等价于
?
?
?
<listitem a:bind=" datasource='author',selected" label=""/>
?
若没有指定注释名称,则假定名称为default
。例如,下面的代码片断使用了default
作为注释名称为label
属性注释,且注释有一个属性,名称和值分别为value
和selected.name
。
?
?
?
<listitem label="@{selected.name}"/>
?
换言之,等价于下面的代码片断:
?
?
?
<listitem label="@{default(value='selected.name')}"/>
?
注:你可以使用多个注释为相同的属性注释,如下所示。
?
?
?
<listitem label="@{ann1(selected.name) ann2(attr2a='attr2a',attr2b)}"/>
?
注释组件声明的简单方式
类似的,你可以通过为一个名为self
的特定属性指定注释表达来注释一个组件。
?
?
?
<listitem self="@{bind(each=person)}"/>
?
self
为一个关键字,表示注释被用于注释组件声明,而不是任何属性。换言之,等价于
?
?
?
<a:bind each="person"/> <listitem/>
?
手动注释组件
通过使用org.zkoss.zk.ui.sys.ComponentCtrl
接口的addAnnotation
?方法,你可以在运行时注释一个组件。
?
?
?
Listbox listbox = new Listbox(); listbox.addAnnotation("some", null);
?
获取注释
可以在运行时取回(retrieved back)注释。通常由工具获取,例如数据绑定管理者,而不是应用程序。换言之,为某一特定目的,应用程序注释ZUML页面以告诉工具如何处理组件。
下面为转储(dump)一个组件所有注释的例子:
?
?
?
void dump(StringBuffer sb, Component comp) { ComponentCtrl compCtrl = (ComponentCtrl)comp; sb.append(comp.getId()).append(": ") .append(compCtrl .getAnnotations()).append('\n'); for (Iterator it = compCtrl.getAnnotatedProperties().iterator(); it.hasNext();) { String prop = it.next(); sb.append(" with ").append(prop).append(": ") .append(compCtrl .getAnnotations(prop)).append('\n'); } }