英雄列表范例:删除英雄

接下来介绍「删除英雄」的实作方法。

删除介面设计

我规划是在每个项目後面增加一个删除按钮,按下该按钮就删除该项目,简单明确。
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> 外,等使用者选了一个英雄後再启用,这样就不需要转发事件。


<<:  C槽系统目录--Windows资料夹常识

>>:  [Day 11] -『 GO语言学习笔记』- switch 叙述

11. STM32-SPI Nokia 5110 LCD 实作

Nokia 5110 LCD 介绍 刚好手边有块Nokia 5110 LCD 就拿它来做测试吧~虽然...

Day5 HTML 语法简易介绍(二)

常见的 HTML elements 标题 headings 在 VS code 中输入以下程序码 &...

【Day24】 Transformer 实作包(一)

开始施工 本来是想分享自己参考网路作法再改写出来的 Transformer,但後来发现自己的架构并不...

[day26] 从Line查询购物车(Rich Menu & Postback)

由左到右,由上至下,分别是 早餐选单按钮 午餐选单按钮 饮料选单按钮 使用说明按钮 查询购物车内容...

Android Studio初学笔记-Day15-ListView

ListView Listview是个能装载多个view的列表元件,在资料的展示时很常被使用。Lis...