ID空间

将视觉表现(visual presentation)分为几个ZUML页面是很常见的。例如,一个页面用来展示订购单,一个对话框用于进入付款期。如果同一个桌面内所有的组件都是非常明确的,开发人员必须为这个桌面内所有页面维护所有标识的唯一性。为了解决这个问题,ID空间的概念被引入。一个ID空间是一个桌面的组件的子集。唯一性只在ID空间的范围内有保障。

Id空间的最简单形式是一个window(org.zkoss.zul.Window)。所有window衍生出来的组件(包括window)形成了一个独立的ID空间。因此,你可以将window作为每个页面的最高组件使用,这样,开发人员需要维护每个页面的唯一性。

更一般地说,任何组件可形成一个ID空间,只要它实现了org.zkoss.zk.ui.IdSpace接口。页面(Page)也实现了这个接口,所以它又是个空间所有者(space owner)。

一个ID空间的最高组件即为空间的所有者,可以使用Component接口中的getSpaceOwner方法来获得这个组件。

如果一个称为X的ID空间,从另外一个称为Y的ID空间衍生而来,那么X的所有者是空间Y的一部分,但从X衍生出来的部分并不是空间Y的一部分。

就像在图中描绘的一样,有三个空间:P,A 和C。空间P包括P,A,F和G。空间A包括A,B,C和D。空间C包括C和E。

在相同ID空间内的组件称为fellows,例如A,B,C和D就是同一ID空间内的fellows。

为了获得另一个fellow,可以使用IdSpaceComponent接口中的getFellow方法。

请注意可以getFellow方法可以被同一ID空间内任何组件调用,并不仅限于空间所有者。同样,对于在同一空间内的任何组件,getSpaceOwner方法返回的是同样的对象,与是否是空间所有者无关。

org.zkoss.zk.ui.Path类提供了在ID空间内简化定位组件的工具。其使用凡是类似java.io.File

Path.getComponent("/A/C/E");
new Path("A/C", "E").getComponent();

命名空间和ID空间

为了能让解释器(interpreter)直接读取到组件,命名空间(org.zkoss.scripting.Namespace)的概 念被引入。首先,每一个ID空间都有一个确切的命名空间。第二,定义在命名空间内的变量对于属于 同一个命名空间的脚本代码及EL表达式是可见的。

<window border="normal">
   <label id="l" value="hi"/>
   <zscript>
      l.value = "Hi, namespace";
   </zscript>
   ${l.value}
</window>

下面的例子有两个命名空间,一个属于window w1,另一个属于window w2b1按钮的onClick事件针对window w1内定义的label,而b2按钮的onClick事件是针对窗口window w2 [24]内的定义的checkbox。

<window id="w1">
   <window id="w2">
      <label id="c"/>
      <button id="b1" onClick="c.value = &quot;OK&quot;"/>
   </window>
   <checkbox id="c"/>
   <button id="b2" onClick="c.label = &quot;OK&quot;"/>
</window>

请注意命名空间是有等级的。换言之,window w2中的zscript可以看见window w1中的组件,除非她凌驾于window w2。因此,在下面的例子中button b1将会改变标签c

<window id="w1">
   <window id="w2">
      <button id="b1" onClick="c.value = &quot;OK&quot;"/>
   </window>
   <label id="c"/>
</window>

除了ZK指定的添加到命名空间的组件,你可以指定自己的变量通过使用setVariable方法,这样 zscript可以直接参考(reference)它们。

zscript中定义变量和函数

除了执行代码,你可以在zscript元素中直接定义变量和函数,就像下面描绘的一样:

<window id="A>
   <zscript>
      Object myvar = new LinkedList();
      void myfunc() {
         ...
      }
   </zscript>
   ...
   <button label="add" onClick="myvar.add(some)"/>
   <button label="some" onClick="myfunc()"/>
</window>

zscript中定义的变量和函数存储在相应脚本语言的解释器(interpreter )中。

zscriptEL表达式

就像命名空间[25]一样,定义在zscript中的变量对于EL表达式都是可见的。

<window>
   <zscript>
   String var = "abc";
   self.setVariable("var2", "xyz", true);
   </zscript>
   ${var} ${var2}
</window>

等价于:

<window>
abc xyz
</window>

请注意,定义在zscript中的变量比定义在命名空间中的变量有更高的优先级。

<window>
   <zscript>
   String var = "abc";
   self.setVariable("var", "xyz", true);
   </zscript>
   ${var}
</window>

等价于:

<window>
abc
</window>

但如果你之后声明了一个同名组件,这就会令人困惑,就像下面展示的那样。

<window>
   <zscript>
   String var = "abc";
   </zscript>
   <label id="var" value="A label"/>
   ${var.value} <!-- Wrong! var is "abc", not the label -->
</window>

因此,建议使用一些命名方式来避免这种困惑。例如,你可以为所有的解释(interpreter)变量加上前缀zs_

另外,应该尽量使用局部变量。局部变量是和类名一起被声明的,并且只对某一范围的zscript代码可见。

<zscript>
Date now = new Date();
</zscript>

你可以通过将其放在{}中使局部变量对于EL表达式不可见,如下:

<zscript>
{ //create a new logic scope
   String var = "abc"; //visible only inside of the enclosing curly brace
}
</zscript>

多范围(Multi-Scope)的解释器

依靠实现,一个解释器或许有确切的范围,或每个ID空间一个逻辑范围来存储这些变量和方法。出于 以上的描述,我们将它们分别称为单范围和多范围的解释器(single-scope and multi-scope interpreters)。

Java解释器(BeanShell)是一个典型的多范围解释器。[26]。它为每个ID空间创建一个独立的解释范围。例如,在下面的例子中分别为window AB创建两个逻辑范围。因此在下面的例子中,var2仅对于window B是可见的,var1对于窗口AB都是可见的。

<window id="A">
   <zscript>var1 = "abc";</zscript>
   <window id="B">
      <zscript>var2 = "def";</zscript>
   </window>
</window>
Java解释器(BeanShell)

通过Java解释器,你可以为一个最近ID空间(例如一个窗口window)的逻辑范围声明一个局部解释变量(interpreter variable)通过指定类名,如下例所示:

<window id="A">
   <window id="B">
      <zscript>
   String b = "local to window B";
      </zscript>
   </window>
</window>

下面是一个更复杂的例子,可以产生abc def

<window id="A">
   <zscript>
   var1 = var2 = "abc";
   </zscript>
   <window id="B">
      <zscript>
   Object var1 = "123";
   var2 = "def";
   var3 = "xyz";
      </zscript>
   </window>
   ${var1} ${var2} ${var3}
</window>

Object var1 ="123"实际上是为window B创建了一个局部变量,对象是指定的。另一方面,var2 ="def"会使解释器(interpreter)在当前或更高的范围内寻找名称为var2的变量。var2变量在window A内已被定义,变量在此被重定义(overrided)。var3 ="xyz "为窗口(winodw)B创建了一个局部变量,而window A并没有定义任何叫做var3的变量。

单范围(Single-Scope)解释器

Ruby, Groovy 和javaScript解释器(Interpreters)并不支持多范围[27]。这就意味着定义的所有变量,就是说, Ruby存储在一个逻辑范围内(每一个解释器)。因此,定义在一个窗口中的解释变量(interpreter variables)会覆盖定义在另一个窗口中的变量,如果它们在同一个页面内。为了避免这种困惑,你可以为每个变量的名字加上与窗口相关的特殊前缀。

[提示]:每个页面都有它自己的解释器(interpreter)来为zscript代码赋值,如果一个桌面有多个页面,那么它或许有多个解释器的实例(instances of the interpreters)(每一种脚本语言)。

在一个页面中使用多种脚本语言

每种脚本语言都与一种解释器(interpreter)相关联。因此,定义在一种语言中的变量和方法对于另外一种语言是不可见的。例如在下面的例子中,变量var 1var2属于两种不同的解释器(interpreter)。

<zscript language="Java">
   var1 = 123;
</zscript>
<zscript language="JavaScript">
   var2 = 234;
</zscript>

getVariableVS getZScriptVariable

可以通过getVariable方法获得定义在命名空间内的变量。另一方面,定义在zscript中的变量是解释它的解释的一部分,它们不是任何命名空间的一部分。换句话说,你不能通过getVariable方法获取它。

你必须使用getZScriptVariable方法来获得zscript中的定义的变量。同样,可以使用getZScriptClassgetZScriptMethod方法来获取定义在zscript中的类和方法。这些方法将会遍历所有的被加载的解释器直到指定的一个被找到。

如果你想找到某个解释器,可以使用getInterpreter方法先获得解释器,就像下面一样:

<zscript>
   var1 = 123; //var1 belongs to the interpreter, not any namespace
   page.getVariable("var1"); //returns null
</zscript>

相反,你必须使用getZScriptVariable方法来获得zscript中的定义的变量。同样,可以使用getZScriptClassgetZScriptMethod方法来获取定义在zscript中的类和方法。这些方法将会遍历所有的被加载的解释器(interpreter)知道指定的一个被找到。

如果你想找到某个解释器,可以使用getInterpreter方法先获得解释器,就像下面一样:

page.getInterpreter("JavaScript").getVariable("some"); //interpreter for JavaScript
page.getInterpreter(null).getVariable("some"); //interpreter for default language


[24] window 实现了 org.zkoss.zk.ui.IdSpace, 所以它形成了一个独立的ID空间和命名空间。

[25] org .zkoss.zk.scripting.Namespace

[26] Java 解释器支持多范围(multi-scope )在 2.3.1 (包括)之后及2.2.1 (包括)之前

[27] 在不久的将来我们或许会支持。