将视觉表现(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,可以使用IdSpace
或Component
接口中的getFellow
方法。
请注意可以getFellow
方法可以被同一ID空间内任何组件调用,并不仅限于空间所有者。同样,对于在同一空间内的任何组件,getSpaceOwner
方法返回的是同样的对象,与是否是空间所有者无关。
org.zkoss.zk.ui.Path
类提供了在ID空间内简化定位组件的工具。其使用凡是类似java.io.File
Path.getComponent("/A/C/E"); new Path("A/C", "E").getComponent();
为了能让解释器(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 w2
。b1
按钮的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 = "OK""/> </window> <checkbox id="c"/> <button id="b2" onClick="c.label = "OK""/> </window>
请注意命名空间是有等级的。换言之,window w2
中的zscript
可以看见window w1
中的组件,除非她凌驾于window w2
。因此,在下面的例子中button b1
将会改变标签c
。
<window id="w1"> <window id="w2"> <button id="b1" onClick="c.value = "OK""/> </window> <label id="c"/> </window>
除了ZK指定的添加到命名空间的组件,你可以指定自己的变量通过使用setVariable
方法,这样 zscript
可以直接参考(reference)它们。
除了执行代码,你可以在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 )中。
就像命名空间[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>
依靠实现,一个解释器或许有确切的范围,或每个ID空间一个逻辑范围来存储这些变量和方法。出于 以上的描述,我们将它们分别称为单范围和多范围的解释器(single-scope and multi-scope interpreters)。
Java解释器(BeanShell)是一个典型的多范围解释器。[26]。它为每个ID空间创建一个独立的解释范围。例如,在下面的例子中分别为window A
和B
创建两个逻辑范围。因此在下面的例子中,var2
仅对于window B
是可见的,var1
对于窗口A
和B
都是可见的。
<window id="A"> <zscript>var1 = "abc";</zscript> <window id="B"> <zscript>var2 = "def";</zscript> </window> </window>
通过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
的变量。
Ruby, Groovy 和javaScript解释器(Interpreters)并不支持多范围[27]。这就意味着定义的所有变量,就是说, Ruby存储在一个逻辑范围内(每一个解释器)。因此,定义在一个窗口中的解释变量(interpreter variables)会覆盖定义在另一个窗口中的变量,如果它们在同一个页面内。为了避免这种困惑,你可以为每个变量的名字加上与窗口相关的特殊前缀。
[提示]:每个页面都有它自己的解释器(interpreter)来为zscript代码赋值,如果一个桌面有多个页面,那么它或许有多个解释器的实例(instances of the interpreters)(每一种脚本语言)。
每种脚本语言都与一种解释器(interpreter)相关联。因此,定义在一种语言中的变量和方法对于另外一种语言是不可见的。例如在下面的例子中,变量var
1
和var2
属于两种不同的解释器(interpreter)。
<zscript language="Java"> var1 = 123; </zscript> <zscript language="JavaScript"> var2 = 234; </zscript>
可以通过getVariable
方法获得定义在命名空间内的变量。另一方面,定义在zscript
中的变量是解释它的解释的一部分,它们不是任何命名空间的一部分。换句话说,你不能通过getVariable
方法获取它。
你必须使用getZScriptVariable
方法来获得zscript
中的定义的变量。同样,可以使用getZScriptClass
和getZScriptMethod
方法来获取定义在zscript
中的类和方法。这些方法将会遍历所有的被加载的解释器直到指定的一个被找到。
如果你想找到某个解释器,可以使用getInterpreter
方法先获得解释器,就像下面一样:
<zscript> var1 = 123; //var1 belongs to the interpreter, not any namespace page.getVariable("var1"); //returns null </zscript>
相反,你必须使用getZScriptVariable
方法来获得zscript
中的定义的变量。同样,可以使用getZScriptClass
和getZScriptMethod
方法来获取定义在zscript
中的类和方法。这些方法将会遍历所有的被加载的解释器(interpreter)知道指定的一个被找到。
如果你想找到某个解释器,可以使用getInterpreter
方法先获得解释器,就像下面一样:
page.getInterpreter("JavaScript").getVariable("some"); //interpreter for JavaScript page.getInterpreter(null).getVariable("some"); //interpreter for default language