跳轉到內容

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

華夏公益教科書