当我们设计页面发现有些元件的组合重复出现时,虽然可以直接「复制—贴上」,但是如果使用范本插入,会更符合物件导向设计中的「重用」原则 (或 DRY)。而且未来若是范本修改,所有插入范本的页面也都自然跟着更新,若是原本采用「复制—贴上」,就得一个一个页面去搜寻修改。
假设我们发现 <textbox>
+ <button>
这个 pattern 经常出现,就可以把它定义成一个范本并指定一个不重复的名字,光是定义范本并不会显示在页面上,而是要用 <apply>
插入。
<apply template="one-field"/>
<template name="one-field">
<textbox/><button label="送出" style="margin-left: 5px"/>
</template>
范本可以定义在页面上任何地方,<apply>
都可以透过指定范本名称来插入,ZK 会将范本内定义的片段插入到 <apply>
的位置。
若我事先在范本内指定几个 EL 变数,插入的时候根据不同的情境再传入不同的值的话,可使范本可应用的情境更广,变得更模组化。
例如我可以把「提示讯息」、「按钮文字」作为参数输入,这样每次使用时该范本时,我都可以调成不同的文字:
<apply template="one-field2" hint="请输入:" label="OK"/>
<template name="one-field2">
${hint}<textbox/><button label="${label}" style="margin-left: 5px"/>
</template>
hint
, label
,对应到范本内的2个变数。不过这种方式传的参数并没有编译器帮你检查名称或数量,要自己检查避免打错字同一份资料有时候有不同的呈现方式,例如个人资料,可以是「显示模式」或「编辑模式」,就可以透过切换两种不同的范本来做到模式切换效果。
假设我要在呈现英雄的个人资料画面做出可以立即切换成编辑模式的编辑器:
阅读模式
编辑模式
首先先定义两种范本:normal, edit
<template name="normal">
<label value="${hero.id}"/>
<label value="${hero.name}"/>
<label value="${hero.age}"/>
</template>
<template name="edit">
<label value="${hero.id}"/>
<textbox id="nameBox" value="${hero.name}" width="100%"/>
<intbox id="ageBox" value="${hero.age}"/>
</template>
再透过按钮去切换不同的范本:
<div style="width: 50%" apply="quickstart.shadow.ProfileEditorComposer">
<button label="Edit"/>
<vlayout style="border: solid 2px; border-radius:5px; width: 150px; padding:5px; margin: 5px">
<apply id="profile" template="normal" hero="${hero}" dynamicValue="true"/>
</vlayout>
</div>
public class ProfileEditorComposer extends SelectorComposer {
@Wire("::shadow#profile")
private Apply apply;
@Wire
来取得参考,selector 语法为 ::shadow
代表所有的 shadow 元件,加上 # ID 选择器,代表 ID 为 profile 的 shadow 元件@Override
public void doBeforeComposeChildren(Component comp) throws Exception {
super.doBeforeComposeChildren(comp);
hero = HeroService.create("奇异博士");
Sessions.getCurrent().setAttribute("hero", hero);
}
doBeforeComposeChildren()
也是一个生命周期方法,根据名称就可得知它是在子元件被建构前被呼叫。这次我覆写 doBeforeComposeChildren()
是因为我要在范本中的元件产生前就把资料准备好,这样 EL 才能存取到 attribute。Sessions.getCurrent()
是 ZK 提供的方法,让我可以取得现有 request 所属的 session。请注意我存入的键值为 hero
,也就是我在 zul 上用 EL 存取的变数名。<apply id="profile" template="normal" hero="${hero}" dynamicValue="true"/>
${hero}
把参数传入范本,那这个变数定义在哪呢?ZK 解析 EL 变数时,会从较小的范围(scope) 一直解析到大的范围,因此若是 zscript 中没有定义 hero 这个变数,ZK 会持续在 session, application 中的 attribute 寻找 key 值为 hero 的物件实作点击按钮来切换模式
private boolean isEdit = false;
@Listen("onClick = button")
public void switchMode(){
if (isEdit){
hero.setName(((Textbox)modeButton.getFellow("nameBox")).getValue());
hero.setAge(((Intbox)modeButton.getFellow("ageBox")).getValue());
}
isEdit = !isEdit;
modeButton.setLabel(isEdit ? "Save": "Edit");
apply.setTemplate(isEdit ? "edit" : "normal");
apply.recreate();
}
@Listen
注册 onClick 倾听器setTemplate()
来切换不同的范本,并呼叫 recreate()
来重建范本内容另一种应用是将范本定义为一个布局范本,然後在每个页面中插入,可以使得页面的布局一致。例如我设计一个由分成标头、内容、页尾3部分的布局范本:
layout-template.zul
<borderlayout height="100%">
<north size="20%" style="background-color:#5c5480">
<div sclass="center">header</div>
</north>
<center style="background-color:#738096">
<apply template="content"/>
</center>
<south size="5%" style="background-color:#5c5480">
<div sclass="center">footer</div>
</south>
</borderlayout>
content
当要插入该布局范本时,才定义 content
这个范本的内容:
<apply templateURI="layout-template.zul">
<template name="content">
<div sclass="center">本页面内容</div>
</template>
</apply>
这样就可以做到,既维持页面在同一个3分布局,又能弹性决定每个页面的内容。
以上几个例子可以帮你更好的重用你的页面,避免「复制—贴上」这种容易产生 bug 的行为。
>>: 新新新手阅读 Angular 文件 - pathMatch(4) - Day30
群益 API 是利用自己开发的程序,结合群益 API 在群益券商下单的一种方式,通常是做程序交易下单...
好的! 我们今天主要要来设定Fab,以及跳页的动画! 1.先在我们的DashboardFragme...
今天是自己开始铁人赛的第一天,目标其实很简单,想要把之前学到的,之前没学到的 全部整合整理成一个作品...
-安全内核 一张图片胜过千言万语。访问控制矩阵可以被视为授权数据(权利和许可)的逻辑“存储库”,由...
Progressive Web App (PWA) 使用当下最新潮的 Web API,Progres...