iT邦幫忙

2021 iThome 鐵人賽

DAY 10
0
Modern Web

ZK 30天速成系列 第 10

英雄列表範例:刪除英雄

接下來介紹「刪除英雄」的實作方法。

刪除介面設計

我規劃是在每個項目後面增加一個刪除按鈕,按下該按鈕就刪除該項目,簡單明確。
https://ithelp.ithome.com.tw/upload/images/20210925/20050621jNwEWCzE1O.jpg

首先要調整 Listbox 中的範本:

<listbox id="heroBox" rows="5" emptyMessage="無資料" nonselectableTags="">
    <listhead>
        <listheader width="50px"/>
        <listheader width="50%"/>
        <listheader/>
    </listhead>
    <template name="model">
        <listitem>
            <listcell label="${forEachStatus.index}"/>
            <listcell label="${each.name}"/>
            <listcell style="text-align:right">
                <button iconSclass="z-icon-times" forward="heroBox.onDelete"/>
            </listcell>
        </listitem>
    </template>
</listbox>
  • 比起之前,增加第三個 <listheader>,範本中第三個 <listcell> 增加一個按鈕,并加上一個 X 圖示,代表刪除。<listcell>可以內包任何元件。
  • nonselectableTags 的元件預設值會讓使用者點擊listitem 內的按鈕時,不會選取該 listitem,但我設定 nonselectableTags="",就代表不管點listitem 內的任一元件,就會同時選取該 listitem,這會讓我知道使用者到底點的是哪個listitem 內的刪除按鈕
  • forward 那一行代表的意義是:將 onClick 事件轉發成 onDelete 事件到 heroBox 上,這個原因是跟傾聽器綁定的時間點有關,我會在下一節說明。

實作刪除傾聽器

動態生成的元件無法用 @Listen

假如按照我之前介紹的觀念,傾聽刪除按鈕的作法是
@Listen("onClick = listcell > button")

但其實行不通,理由請看以下程式碼:

@Override
public void doAfterCompose(Component comp) throws Exception {
    super.doAfterCompose(comp);
    //在此初始化資料或元件
    heroList.addAll(HeroService.getInitHero());
    heroBox.setModel(heroList);
}

@Listen 是由 SelectorComposer 來幫你基於該 annotation 註冊傾聽器,是在super.doAfterCompose(comp) 內執行,但是 listbox 直到 setModel()才根據該 data model 建立對應的 listitem,也就是要註冊傾聽器的時機時,目標 button都還沒產生,根本無法註冊還不存在的元件上,因此要換個做法。

轉發事件

因此利用 ZK 另一個特性「轉發事件」,將 button onClick 事件轉發到父元件 heroBox上,而轉發的時候可以自訂事件名稱,讓轉發後的事件跟ZK預設事件名稱區隔開來,也有利於辨識,因此我才改轉發成 onDelete 事件,語法如下:

[原事件]=[元件ID].[新事件]

而原事件如果是 onClick 可以省略,因此就可以寫成:

<button iconSclass="z-icon-times" forward="heroBox.onDelete"/>

因此,當使用者點下按鈕,ZK 就會轉發成 onDelete 事件到 heroBox 上。而 heroBox 是先於綁定傾聽器前生成的,因此我可以註冊傾聽器在上面:

@Listen("onDelete = #heroBox")
public void delete(){
    heroList.remove(heroList.getSelection().iterator().next());
}
  • 依如上語法註冊傾聽器後,一點按鈕就會呼叫 delete()
  • 因為先前設定了 nonselectableTags,一點刪除按鈕就會選擇同一個 listitem 中的英雄,而 ListModelList 也儲存了選擇狀態,只要 heroList.getSelection().iterator().next() 就能取得被選擇的 Hero 物件
  • 呼叫 remove() 將該 Hero 移除,依照先前所提過的 model-driven rendering,ListModelList 會發事件通知 Listbox 把這變動的資料繪製到瀏覽器上,因此你會看到被選的英雄就消失了

即便不是動態產生的元件,也可以用轉發事件,它能讓你把子元件的的事件都轉發到同一個父元件上,讓你可以一致地註冊傾聽器在該父元件上,不用根據不同動作註冊在不同元件上,一但未來想要變動子元件,只要轉發的事件維持相同,也不需要更改註冊的方式。再來你可以將事件名稱 (event name) 改成更符合應用情境的名稱 (onDelete, onProjectClose) 增加可讀性、可維護性。

刪除功能的實作與新增類似,都是傾聽事件之後,修改 ListModelList 來更新頁面。這也是 ZK 的主要強項,讓你專注在後端的邏輯,跟前端的溝通與重繪都由 ZK 幫你處理了。

以上描述的作法只是其一,你也可以把刪除按鈕拿出 <listbox> 外,等使用者選了一個英雄後再啟用,這樣就不需要轉發事件。


上一篇
英雄列表範例:新增英雄
下一篇
英雄列表範例:修改英雄
系列文
ZK 30天速成30

尚未有邦友留言

立即登入留言