跳转到内容

ZK/操作指南/常见问题

来自维基教科书,开放世界中的开放书籍

如何访问另一个应用程序中的资源?

[编辑 | 编辑源代码]

1. 假设您有另一个名为 app2 的应用程序。然后,您可以通过使用 “~app2/your/resource/path” 来访问资源。

例如:

<include src="~app2/main/foo.zul"/>

请注意,Web 容器可能会阻止您访问其他 Web 应用程序。在这种情况下,将抛出 org.zkoss.lang.SystemException(("Context not found or not visible to....")。要启用它,您必须正确配置 Web 容器。

2. 在 context.xml 中,为 Context 元素指定 crossContext="true"

<Context crossContext="true">

如何添加超链接?

[编辑 | 编辑源代码]

使用 toolbarbuttonbutton 组件并指定 href 属性。例如,以下代码将生成 ZK - 简单而丰富 超链接。

<toolbarbutton label="ZK - Simple and Rich" href="http://tw.yahoo.com"/>
<button label="Rich Internet Application" href="http://www.zkoss.org/zkdemo/userguide"/>

您也可以将 xhtml 和 xul 混合在一起,以便您可以创建普通的超链接,例如:

<window title="mix HTML demo" xmlns:h="http://www.w3.org/1999/xhtml">
<button label="zul hyperlink button" href="http://www.google.com/"/>
<h:a href="http://www.google.com">xhtml hyperlink</h:a>
</window>

查看演示和开发者指南以获取更多示例。

如何将 AWT Image 转换为 ZK Image?

[编辑 | 编辑源代码]
import java.io.ByteArrayOutputStream;
import java.awt.image.RenderedImage;
import javax.imageio.ImageIO;
import org.zkoss.image.Image;
import org.zkoss.image.AImage;

Image encode(RenderedImage image) {
  ByteArrayOutputStream os = new ByteArrayOutputStream();
  ImageIO.write(image, "png", os);
  return new AImage("my-image.png", os.toByteArray());
}

从 3.0.7 和 3.5.0 开始,有一个名为 org.zkoss.image.Images 的实用程序类来简化编码工作。

如何将同一个页面包含两次?

[编辑 | 编辑源代码]

使用 include 组件,您可以像下面这样将任何页面包含多次。

<include src="/mypage.zul"/>
<include src="/mypage.zul"/>

但是,如果您想访问它们内部的组件,您必须为被包含的页面分配一个唯一的标识符。您可以这样做。

<include src="/mypage.zul?pageId=first"/>
<include src="/mypage.zul?pageId=second"/>

此外,在被包含的页面中,即此示例中的 mypage.zul,您必须编写

<?page id="${param.pageId}"?>

然后,您可以访问它们的组件。

Path.getComponent('//first/asdf/'); 
Path.getComponent('//second/asdf/'); 

请注意,组件的创建是在渲染阶段的最后,所以您只能在以下事件的事件监听器中访问它们

<window>
<zscript><![CDATA[
        /**
         * in a real application we would use something like 
         *         List iterateOverMe = sessionScope.get("listToRender");
         */
        String[][] iterateOverMe = {
                {"99", "Fred Flintstone"}
                ,{"8", "Wilma Flintstone"}
                ,{"65", "Barney Rubble"}
                ,{"32", "Betty Rubble"}
        };
]]></zscript>
<tabbox mold="accordion">
<tabs>
 <!-- more realisticly my iterateOverMe would be a List of 
 pojos so that I can write ${each.label} -->
 <tab forEach="${iterateOverMe}" label="${each[1]}"/>
</tabs>
<tabpanels>
 <!-- more realisticly my iterateOverMe would be a List of 
 pojos so that I can write ${each.id} -->
 <tabpanel forEach="${iterateOverMe}" >
 <include src="/render-item.zul?pageId=${each[0]}"/>
 </tabpanel>
</tabpanels>
</tabbox>
</window>

在该页面中,我们为搜索结果列表中的每个对象拉入 search-item.zul 一次,并将包含的页面提供一个页面 ID,该 ID 是要渲染的项目的标识符,即 99、8、65、32。在 render-item.zul 中

<?page id="${param.pageId}"?>
<zscript>
 // Here we have to use param.pageId to locate the object that we will render
 Object itemToRender = ... // use param.pageId as the identifier to locate specific object to render at this time
</zscript>
<vbox>
 <button label="${itemToRender.label}"/>
</vbox>

该文件被包含了四次,param.pageId 每次都不同,即 99、8、65、32。每次调用页面时,我们使用 param.pageId 来查找要渲染的业务项目。

如何初始化一个组件,如果将其扩展到自定义类?

[编辑 | 编辑源代码]

假设我们用 MyTabbox 扩展 Tabbox,如下所示。

public class MyTabbox extends Tabbox {
...

然后,您可以使用两种方法对其进行初始化:构造函数和 onCreate 事件。

public class MyTabbox extends Tabbox {
  public MyTabbox() {
    ...//some init
  }
  public void onCreate() {
    ...//other init
  }
}

构造函数是在成员被分配初始值之前调用的,而 onCreate 事件是在整个页面加载后处理的。

但是,如果您以编程方式创建组件,而不是在 ZUML 页面中声明,则不会发送 onCreate 事件。

如何在处理事件时重定向到另一个页面?

[编辑 | 编辑源代码]

使用 Execution 接口的 sendRedirect 方法来要求浏览器重定向到另一个页面。对当前页面/桌面的所有更新都将被丢弃。

void onClick() {
  if (some_condition)
    Executions.sendRedirect("another_page");
}

如何只刷新内部页面?

[编辑 | 编辑源代码]

首先,使用 include 组件并指定 src 属性来包含您想要的任何页面(ZK、JSP、JSF 或其他)在 ZK 页面中。其次,您可以通过更改 src 属性来动态地更改它。例如,以下代码将在最终用户按下 Bye! 按钮时将内部页面从 hello.zul 更改为 byebye.zul。

<include id="inner" src="hello.zul"/>
<button label="Bye!" onClick="inner.src = &quot;byebye.zul&quot;"/>

如果您只想重新加载同一个页面(而不是更改到另一个页面),您必须先将 src 属性设置为 null;然后将 src 属性设置回它以前的值。因为 ZK 优化了操作,所以将相同的值设置为同一个属性将被视为什么都不做。例如,以下代码将在最终用户按下 Reload 按钮时刷新 hello.zul 页面。

<include id="inner" src="hello.zul"/>
<button id="reload" label="Reload" onClick="String tmp=inner.src; inner.src=null; inner.src=tmp;"/>

如果您想在完成其他一些工作后以编程方式从另一个按钮的事件处理程序中调用 Reload 按钮,您可以通过使用以下方法将当前事件分派到该按钮,只需按 ID 引用它

Events.sendEvent(reload,event)

如下所示

<button label="Do Lots Of Stuff Then Pass The Click To The Reload Button">
<attribute name="onClick">
{
 doLotsOfStuff();
 Events.sendEvent(reload, event);
} 
</attribute>
</button>

如何延迟加载选项卡以加快超大型页面的加载速度?

[编辑 | 编辑源代码]

创建以下页面,以及另外两个页面 "tab1.zul" 和 "tab2.zul" 在同一个文件夹中。在 tab1.zul 和 tab2.zul 中放一个 <window>,包含您想要的任何 zul(提示,访问演示页面,点击 "Try Me" 按钮,剪切粘贴一些大型源代码)。

<?page id="main-page"?>
<window id="main-window">
<tabbox width="400px">
	<attribute name="onSelect">{
		//alert("selected index:"+self.selectedIndex);
		if(self.selectedIndex > 0 ) {
			Include inc = (Include)Path.getComponent("//main-page/main-window/tab"+self.selectedIndex);
			inc.setSrc("tab"+self.selectedIndex+".zul");
		}
	}</attribute>
	<tabs>
		<tab label="Tab 0"/>
		<tab label="Tab 1"/>
		<tab label="Tab 2"/>
	</tabs>
	<tabpanels>
		<tabpanel>This is panel 0 and it does not have an include</tabpanel>
		<tabpanel>
			<include id="tab1" src=""/> 
		</tabpanel>
		<tabpanel>
			<include id="tab2" src=""/> 
		</tabpanel>
	</tabpanels>
</tabbox>
</window>

只有当您点击 "Tab 1" 和 "Tab 2" 时,tab1.zul 和 tab2.zul 的内容才会被加载,并且它们的组件会被创建。为了验证这一点,请注释掉以以下行开头的代码

inc.setSrc("tab"+self.selectedIndex+".zul");

然后刷新页面,点击选项卡,您将看不到 tab1.zul 或 tab2.zul 中的任何 xul,因为在注释掉加载它们的事件处理程序代码之后,这些文件不会被加载。

如何指定标签属性中的换行符?

[编辑 | 编辑源代码]

XML解析器将换行符视为普通的空格,因此使用换行符将不起作用。

<label value="line 1
line 2"/>

请改为使用属性元素,如下所示:

<label multiline="true">
  <attribute name="value">line 1
line 2</attribute>
</label>

如何在 onXxx 事件处理程序中使用 "each" 变量?

[编辑 | 编辑源代码]

示例 1(错误)

[编辑 | 编辑源代码]
 <window title="Countries" border="normal" width="100%">
     <zscript><![CDATA[
         // Here we have an array of countries
         // We will generate a bunch of buttons per this array
         // When the button is click, an alert window will show the country name.
         
         String[] countries = {"China", "France", "Germany", "United Kingdom", "United States"};
     ]]></zscript>
     
     <hbox>
          <!-- WARNING THIS IS INCORRECT USE "Example 3" -->
         <button label="${each}" forEach="${countries}" onClick="alert(${each})"/>
     </hbox>
 </window>

示例 1 是错误的。EL 表达式不能在 onXxx() 事件处理程序中使用。当按钮被点击时,它会抛出一个异常 "Attempt to access property on undefined variable or class name",因为 onClick 中的 alert(${each}) 被直接解释为 Java 代码。

示例 2(错误)

[编辑 | 编辑源代码]
 <window title="Countries" border="normal" width="100%">
     <zscript><![CDATA[
         // Here we have an array of countries
         // We will generate a bunch of buttons per this array
         // When the button is click, an alert window will show the country name.
         
         String[] countries = {"China", "France", "Germany", "United Kindom", "United States"};
     ]]></zscript>
     
     <hbox>
          <!-- WARNING THIS IS INCORRECT USE "Example 3" -->
         <button label="${each}" forEach="${countries}" onClick="alert(each)"/>
     </hbox>
 </window>

示例 2 将 alert(${each}) 重写为 alert(each)。因为 each 是一个隐式对象,所以它应该可以直接在 onClick 中使用,就像其他隐式对象一样。然而,点击按钮仍然会抛出一个异常 "Undefined argument: each"。原因很简单。虽然 each 是一个隐式对象,但它是一个 临时 隐式对象。它只在 zuml 页面被评估时存在。当按钮被点击时,那个最初的 each 已经不存在了。

那么如何在 onXxx() 事件处理程序中引用 forEach 属性的 each 变量呢?关键是在 zuml 页面被评估时存储临时 each 变量,并在 onXxx 事件被触发时使用该存储的变量。

示例 3(正确)

[编辑 | 编辑源代码]
<window title="Countries" border="normal" width="100%">
    <zscript><![CDATA[
        // Here we have an array of countries
        // We will generate a bunch of buttons per this array
        // When the button is click, an alert window will show the country name.
        
        String[] countries = {"China", "France", "Germany", "United Kindom", "United States"};
    ]]></zscript>
    
    <hbox>
        <button label="${each}" forEach="${countries}"
            onClick="alert(componentScope.get(&quot;country&quot;))">
            <custom-attributes country="${each}"/>
        </button>
    </hbox>
</window>

示例 3 使用按钮的自定义属性映射(componentScope)在 zuml 页面被评估时存储临时 each 对象,并在按钮被点击时使用该存储的对象。

如何使 Grid 的行和列可拖放?

[编辑 | 编辑源代码]

由 Bakoma 提供


Grid 由列和行组成,每个列的子元素都是列。类似地,行的子元素是行。

这是一个带有标题列和三行的简单 Grid:

        Name  Age Grade
        Mike  29    C 
        Todd  21    B
        Tony  37    A

以下代码将以上内容放入一个 Grid 中,行和列可拖放和放置。以下是步骤:

  • 创建 Grid
  • 添加列,并指定每列都是可拖放和放置的,前提是它们是相同类型的列。
<columns>  
    <column label="Name" draggable="col" droppable="col" onDrop="move(event.dragged)"/>gunawan
    <column label="Age" draggable="col" droppable="col" onDrop="move(event.dragged)"/> 
    <column label="Grade" draggable="col" droppable="col" onDrop="move(event.dragged)"/> 
</columns>
  • 添加行,并指定每行都是可拖放和放置的。
<rows>  
    <row draggable="row" droppable="row" onDrop="move(event.dragged)">  
        <label value="Mike" />  
        <label value="29" /> 
        <label value="C" /> 
    </row>  
    <row draggable="row" droppable="row" onDrop="move(event.dragged)">  
        <label value="Todd" />  
        <label value="21" /> 
        <label value="B" />
    </row>  
    <row draggable="row" droppable="row" onDrop="move(event.dragged)">  
        <label value="Tony" />  
        <label value="31" /> 
        <label value="A" /> 
    </row> 
</rows>
  • 实现事件处理函数。确保在移动列时也移动相应的单元格。
void move(Component dragged) { 
    if(dragged.getClass().getName().endsWith("Column")) { 
        int maxRows=dragged.getGrid().getRows().getChildren().size(); 
        int i= dragged.getParent().getChildren().indexOf(dragged); 
        int j= self.getParent().getChildren().indexOf(self); 
 
        //move celles for each row 
        for(int k=0; k < maxRows; k++) 
            self.getGrid().getCell(k,j).parent.insertBefore(self.getGrid()
              .getCell(k,i),self.getGrid().getCell(k,j)); 
   } 
 
    self.parent.insertBefore(dragged, self);  
} 
  • 将它们组合在一起以获得完整的代码。
<zk> 
<grid>  
  <columns>  
    <column label="Name" draggable="col" droppable="col" onDrop="move(event.dragged)"/> 
    <column label="Age" draggable="col" droppable="col" onDrop="move(event.dragged)"/> 
    <column label="Grade" draggable="col" droppable="col" onDrop="move(event.dragged)"/> 
  </columns> 
  <rows>  
    <row draggable="row" droppable="row" onDrop="move(event.dragged)">  
        <label value="Mike" />  
        <label value="29" /> 
        <label value="C" /> 
    </row>  
    <row draggable="row" droppable="row" onDrop="move(event.dragged)">  
        <label value="Todd" />  
        <label value="21" /> 
        <label value="B" />
    </row>  
    <row draggable="row" droppable="row" onDrop="move(event.dragged)">  
        <label value="Tony" />  
        <label value="31" /> 
        <label value="A" /> 
    </row> 
  </rows> 
</grid>  
<zscript><![CDATA[ 
  void move(Component dragged) { 
    if(dragged.getClass().getName().endsWith("Column")) { 
        int maxRows=dragged.getGrid().getRows().getChildren().size(); 
        int i= dragged.getParent().getChildren().indexOf(dragged); 
        int j= self.getParent().getChildren().indexOf(self); 
 
        //move celles for each row 
        for(int k=0; k < maxRows; k++) 
           self.getGrid().getCell(k,j).parent.insertBefore(self.getGrid()
              .getCell(k,i),self.getGrid().getCell(k,j)); 
   } 
 
    self.parent.insertBefore(dragged, self);  
  }  
]]></zscript> 
</zk>

如何生成 ©?

[编辑 | 编辑源代码]

<html>版权所有 &amp;copy; Super Co.</html>

解释:&amp; 是一种将 & 传递给 html 组件的 content 属性的方法。并且,html 组件直接将 content 属性输出到浏览器。

另一方面,如果使用 label 组件,它将不起作用,因为 label 会对其内容进行编码。

<label>版权所有 &amp;copy; Super Co.</label>

如何通过程序设置 forward 属性

[编辑 | 编辑源代码]

org.zkoss.zk.ui.sys.ComponentsCtrl.applyForward(comp, "onClick");

如何在 ZK 中启用日志记录

[编辑 | 编辑源代码]

1. 首先,在您的应用程序的 zk.xml 文件中设置日志配置。我使用了空字符串。

<zk>
    <log>
        <log-base></log-base>
    </log>
</zk>

2. 创建一个 i3-log.conf 配置文件,并将它放在 Tomcat 的 /conf 文件夹中。它应该包含以下内容。

org.zkoss.testlog=DEBUG
org.zkoss=ERROR

3. 记住修改 Tomcat 的 /conf 文件夹中的 logging.properties 文件。默认的 ConsoleHandler 的级别是 INFO,因此您不会看到任何 DEBUG 消息,否则。将 java.util.logging.ConsoleHandler.level = INFO 修改为 java.util.logging.ConsoleHandler.level = FINEST 4. 如果您想输出来自程序的特定消息

private static final Log log = Log.lookup(org.zkoss.testlog.Foo.class);
if(log.debugable()){
    log.debug("a log for debug message");
}
if(log.infoable()){
    log.info("a log for info message");
}

来自 http://www.javaworld.com.tw/roller/atticcat/entry/2007_10_10_Using_log_in_zk

华夏公益教科书