`
xiao_feng68
  • 浏览: 104053 次
  • 性别: Icon_minigender_1
  • 来自: 济南
社区版块
存档分类
最新评论

(XF - 1)ZK - MVC 模式

    博客分类:
  • ZK
阅读更多
The ZK Team are always looking for the best practice to realize MVC approachs in ZK. In this article and this article there have been some discusions about MVC programming on ZK. In this article, I am going to discuss in details the three utility classes and three helper methods provided by ZK that will help developers to write applications in Model-View-Controller pattern easily. Besides, they also provides a convenient path for refactoring prototype codes that originally written in zul zscript to pure Java class codes.

I will use a simple example to illustrate the power of these utility classes and helper methods.

The Example
Two input textboxes for the user to input First Name and Last Name. The Full Name will be automatically updated when either input textbox changed.


The Day Before - Implement Own Composer Class

composer1.zul

<window title="composer1 example" border="normal" width="300px" apply="MyComposer1">
    <grid>
        <rows>
            <row>First Name: <textbox id="firstName" forward="onChange=onFirstName"/></row>
            <row>Last Name: <textbox id="lastName" forward="onChange=onLastName"/></row>
            <row>Full Name: <label id="fullName"/></row>
        </rows>
    </grid>
</window>


MyComposer1.java

...
public class MyComposer1 implements Composer {
    private Textbox firstName;
    private Textbox lastName;
    private Label fullName;
  
    public void doAfterCompose(Component win) throws Exception {
        firstName = (Textbox) win.getFellow("firstName");
        lastName = (Textbox) win.getFellow("lastName");
        fullName = (Label) win.getFellow("fullName");

        //define and register event listeners
        win.addEventListener("onFirstName",
            new EventListener() {
                public void onEvent(Event event) throws Exception {
                    fullName.setValue(firstName.getValue()+" "+lastName.getValue());
                }
            });
        win.addEventListener("onLastName",
            new EventListener() {
                public void onEvent(Event event) throws Exception {
                    fullName.setValue(firstName.getValue()+" "+lastName.getValue());
                }
            });
    }
}

Here the composer1.zul defines the View of the application while the MyComposer1 class intervenes in the life cycle of the window creation and is reponsible for the definition and registration of the event listener. However, the more you write these codes, the more you will find the same addEventListener pattern occurs repeatly. So..., "Can we take away those addEventListener codes?"

The org.zkoss.zk.ui.util.GenericComposer utility class is ZK's response to this question.

Just Write those onXxx Event Listener Codes

composer2.zul

<window title="composer2 example" border="normal" width="300px" apply="MyComposer2">
    <grid>
        <rows>
            <row>First Name: <textbox id="firstName" forward="onChange=onFirstName"/></row>
            <row>Last Name: <textbox id="lastName" forward="onChange=onLastName"/></row>
            <row>Full Name: <label id="fullName"/></row>
        </rows>
    </grid>
</window>


MyComposer2.java

...
public class MyComposer2 extends GenericComposer {
    private Textbox firstName;
    private Textbox lastName;
    private Label fullName;
  
    public void doAfterCompose(Component win) throws Exception {
        super.doAfterCompose(win);
 
        //locate ZK components
        firstName = (Textbox) win.getFellow("firstName");
        lastName = (Textbox) win.getFellow("lastName");
        fullName = (Label) win.getFellow("fullName");
 
        //all addEventListener and new EventListener() codes are removed
    }
  
    public void onFirstName(Event event) {
        fullName.setValue(firstName.getValue()+" "+lastName.getValue());
    }
  
    public void onLastName(Event event) {
        fullName.setValue(firstName.getValue()+" "+lastName.getValue());
    }
}

The MyComposer2 class inherits the GenericComposer and write onXxx event listener codes directly. The doAfterCompose method of GenericComposer class will do the tedious addEventListener for you. This is a typical Design by Convention approach. All methods with the onXxx naming pattern would be deemed as an event handling code and is registed automatically.

Auto-wire the Components and Beans
For a typcial interactive rich application, the event handling codes usually have to access data model beans and manipulate UI components to interact with the end users. Getting references to such components and data beans are a common practice in event handling codes. That is why we need those getFellow() codes in MyComposer2 class. On the other hand, these are where a framework can play a role. Why not just bind these components and data beans automatically?

So here comes the org.zkoss.zk.ui.util.GenericAutowireComposer utilty class which extends GenericComposer class and add the auto-wire features.


composer3.zul

<window title="composer3 example" border="normal" width="300px" apply="MyComposer3">
    <grid>
        <rows>
            <row>First Name: <textbox id="firstName" forward="onChange=onFirstName"/></row>
            <row>Last Name: <textbox id="lastName" forward="onChange=onLastName"/></row>
            <row>Full Name: <label id="fullName"/></row>
        </rows>
    </grid>
</window>


MyComposer3.java

...
public class MyComposer3 extends GenericAutowireComposer {
    private Textbox firstName; //auto-wired
    private Textbox lastName; //auto-wired
    private Label fullName; //auto-wired

    //all getFellow() codes are removed

    public void onFirstName(Event event) {
        fullName.setValue(firstName.getValue()+" "+lastName.getValue());
    }
  
    public void onLastName(Event event) {
        fullName.setValue(firstName.getValue()+" "+lastName.getValue());
    }
}

Takes a look of the MyComposer3 class. It extends GenericAutowireComposer and all getFellow() methods can be removed. The doAfterCompose method of GenericAutowireComposer class will inject the proper components to the fields for you. In fact, if you define proper variable resolver for example the Spring variable resolver in your zul file, the GenericAutowireComposer will also inject Spring beans for you.

Following are some snippet codes for that concept. I will discuss ZK + Spring using this approach in another article.


spring-config.xml

...
<bean id="taskDao" class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean">
...


taskEditor.zul

<?variable-resolver class="org.zkoss.zkplus.spring.DelegatingVariableResolver"?>
<window id="taskWnd" title="Task Editor" border="normal" width="500px" apply="TaskEditorComposer">
    <grid>
        <rows>
            <row>Title: <textbox id="title"/></row>
            <row>Description: <textbox id="description"/></row>
        </rows>
    </grid>
    <button id="saveBtn" label="Save" forward="onClick=onSaveTask"/>
</window>
  

TaskEditorComposer.java

public class TaskEditorComposer extends GenericAutowireComposer {
    private TransactionProxyFactoryBean taskDao; //Spring bean auto wired
    private Textbox title; //auto wired
    private Textbox description; //auto wired
    private Button saveBtn; //auto wired
  
    public void onSaveTask(Event event) {
        Task currentTask = componentScope.get("task");
        currentTask.setTitle(title.getValue());
        currentTask.setDescription(description.getValue());      
        taskDao.update(currentTask);
    }
    ...
}

Supporting of Implicit Objects
As the code in zscript can use some implicit objects directly, the GenericAutowrieComposer also supports such implicit objects concept and even the zscript alert() methods. MyComposer4 demonstrates you this feature. Whenever the firstName changed, it will ask self (i.e. the implicit object which is applied the composer; i.e. the Window) to change its title and popup a message alert window. This brings in another benefits. It provides a convenient path for you to easily refactor your prototype zscript codes into product Java class code. You can just copy-and-paste and do some minor modification even if you have used some such implicit objects that used to exists in zscript only. Check the following composer4-1.zul that use zul and zscript only. See how similar of its onFirstName() and onLastName() codes to those of the MyComposer4.java



composer4.zul

<window title="composer4 example" border="normal" width="300px" apply="MyComposer4">
    <grid>
        <rows>
            <row>First Name: <textbox id="firstName" forward="onChange=onFirstName"/></row>
            <row>Last Name: <textbox id="lastName" forward="onChange=onLastName"/></row>
            <row>Full Name: <label id="fullName"/></row>
        </rows>
    </grid>
</window>


MyComposer4.java

public class MyComposer4 extends GenericAutowireComposer {
    private Textbox firstName;
    private Textbox lastName;
    private Label fullName;
  
    public void onFirstName(Event event) {
        fullName.setValue(firstName.getValue()+" "+lastName.getValue());
        ((Window)self).setTitle("First Name changed");
        alert("First Name changed to "+firstName.getValue());
    }
  
    public void onLastName(Event event) {
        fullName.setValue(firstName.getValue()+" "+lastName.getValue());
        ((Window)self).setTitle("Last Name changed");
        alert("Last Name changed to "+lastName.getValue());
    }
}


composer4-1.zul (quick prototyping)

<window title="composer4-1 example" border="normal" width="300px">
    <attribute name="onFirstName"><![CDATA[
         fullName.setValue(firstName.getValue()+" "+lastName.getValue());
        ((Window)self).setTitle("First Name changed");
        alert("First Name changed to "+firstName.getValue());
    ]]></attribute>
  
    <attribute name="onLastName"><![CDATA[
         fullName.setValue(firstName.getValue()+" "+lastName.getValue());
        ((Window)self).setTitle("Last Name changed");
        alert("Last Name changed to "+lastName.getValue());
    ]]></attribute>
  
    <grid>
        <rows>
            <row>First Name: <textbox id="firstName" forward="onChange=onFirstName"/></row>
            <row>Last Name: <textbox id="lastName" forward="onChange=onLastName"/></row>
            <row>Full Name: <label id="fullName"/></row>
        </rows>
    </grid>
</window>
Get Rid of Those "forward" Attributes
Until now, the previous example codes always use the forward attribute in zul file to forward onChange event of the firstName textbox and lastName textbox to the Window as onFirstName and onLastName event, respectively. No doubt these are also repeat patterns in a ZK MVC practice. "Can we remove such forward attributes?"

The org.zkoss.zk.ui.util.GenericForwardComposer utilty class is designed to fulfill this requirement. Note that GenericForwardComposer extends the GenericAutowireComposer class, so it has all features of its parent class(GenericAutowireComposer) and grandparent class(GenericComposer).


composer5.zul

<window title="composer5 example" border="normal" width="300px" apply="MyComposer5">
    <grid>
        <rows>
            <row>First Name: <textbox id="firstName"/></row><!-- forward is removed -->
            <row>Last Name: <textbox id="lastName"/></row><!-- forward is removed -->
            <row>Full Name: <label id="fullName"/></row>
        </rows>
    </grid>
</window>


MyComposer5.java

public class MyComposer5 extends GenericForwardComposer {
    private Textbox firstName;
    private Textbox lastName;
    private Label fullName;
  
    //onChange event from firstName component
    public void onChange$firstName(Event event) {
        fullName.setValue(firstName.getValue()+" "+lastName.getValue());
    }
  
    //onChange event from lastName component
    public void onChange$lastName(Event event) {
        fullName.setValue(firstName.getValue()+" "+lastName.getValue());
    }
}

When writing composer that extends GenericForwardComposer, you have to follow some naming rules on your event listener methods. It is another Design by Convention pattern implementation. Check the MyComposer5 class. The naming of onChange$firstName event listener is read as "onChange event from the firstName component" and the GenericForwardComposer will help you to forward the event to the applied component. As you can see, the forward attributes are removed from the composer5.zul without problems. So now we have a clean separation between the View the .zul file and the Controller the composer codes.

What if I Cannot Inherit from These ZK Utility Classes
In some cases, we might have to inherit our Composer class from another class for some reason. Are we restrictive to have the benefits of these GenericXxxComposer? Not at all! The ZK Team provides three coresponding helper methods to do the same features without requiring the inheritance from those GenericXxxComposer classes. (In this article Qamaralzaman Habeek also suggested one way to register the ZK event listeners automatically.)


composer6.zul

<window title="composer6 example" border="normal" width="300px" apply="MyComposer6">
    <grid>
        <rows>
            <row>First Name: <textbox id="firstName"/></row>
            <row>Last Name: <textbox id="lastName"/></row>
            <row>Full Name: <label id="fullName"/></row>
        </rows>
    </grid>
</window>

MyComposer6.java

public class MyComposer6 implements Composer {
    private Textbox firstName;
    private Textbox lastName;
    private Label fullName;
  
    public void doAfterCompose(Component win) throws Exception {

        //wire variables
        Components.wireVariables(win, this);

        //register onXxx event listeners
        Events.addEventListeners(win, this);
      
        //auto forward
        Components.addForwards(win, this);
    }
  
    public void onChange$firstName(Event event) {
        fullName.setValue(firstName.getValue()+" "+lastName.getValue());
    }
  
    public void onChange$lastName(Event event) {
        fullName.setValue(firstName.getValue()+" "+lastName.getValue());
    }
}

Composer6 example is the refactored codes of the Composer1 example. See the comments on each line. Each helper method provides a feature to remove the boilerplate codes.

org.zkoss.zk.ui.event.Events.addEventListeners(Component comp, Object controller): Add onXxx event listener defined in controller object to the specified component. This helper method registers those onXxx events to the specified component so you don't have to implement and add EventListener into the component one by one. This helper method provides the same feature of the GenericComposer.
org.zkoss.zk.ui.Components.wireVariables(Component comp, Object controller): Wire accessible variable objects of the specified component into a controller Java object. This helper method wire the embedded objects, components, and accessible variables into the controller object per the components' id and variables' name automatically for you so you don't have to call getFellows() or Path.getComponents() etc. to get references. This helper method provides the same feature of the GenericAutowireComposer.
org.zkoss.zk.ui.Components.addForwards(Component comp, Object controller): Adds forward conditions to the source components defined in the controller object so their onXxx events can be forwarded to the specified target component. This helper method searchs those onXxx$id methods defined in the controller object and adds forward condition to the components as specified in id so you don't have to specify in zul file the forward attribute one by one. This helper mehtod provides the same feature of the GenericForwardComposer.
How About if I Use the "use" attribute
Since day one, ZK has provided a way to do MVC by using customized component as a controller. "Can we have the benifits of these three helper methods in such conditions?" Sure you can!


mywindow.zul

<window title="mywindow example" border="normal" width="300px" use="MyWindow">
    <grid>
        <rows>
            <row>First Name: <textbox id="firstName"/></row>
            <row>Last Name: <textbox id="lastName"/></row>
            <row>Full Name: <label id="fullName"/></row>
        </rows>
    </grid>
</window>


MyWindow.java

...
public class MyWindow extends Window implements AfterCompose {
    private Textbox firstName;
    private Textbox lastName;
    private Label fullName;

    //-- AfterCompose --//
    public void afterCompose() {
        //wire variables
        Components.wireVariables(this, this);
      
        //NO need to register onXxx event listeners

        //auto forward
        Components.addForwards(this, this);
    }
    public void onChange$firstName(Event event) {
        fullName.setValue(firstName.getValue()+" "+lastName.getValue());
    }
  
    public void onChange$lastName(Event event) {
        fullName.setValue(firstName.getValue()+" "+lastName.getValue());
    }
}
As you can see, MyWindow actually looks similar to MyComposer6. The difference is that using the use attribute, the component and the controller is of the same object. We need to find a proper timing to call the auto-wire and add-forword helper methods in MyWindow class. In this example, I choose implementing the org.zkoss.zk.ui.ext.AfterCompose interface and call the auto-wire and add-forward helper methods in afterCompose() interface method. The ZK loader engine will call this afterCompose() method after all child components are properly composed just like the doAfterCompose() to the Composer interface. Note that here we don't have to register onXxx event listeners explicitly since the ZK update engine will call onXxx event listeners defined in component automatically.

Download
Download the example codes(.war file).

Summary
ZK programming is all about event handling and UI components manipulation. Apparently, the event listener is where the business logic is all about and it is the controller codes that coordinates everything. To make MVC programming easier, we have made writing the controller codes much easier. So here are the targets we have achieved:

Make writing event listener easier.
Make register event listener easier.
Make accessing UI components and data beans easier.
And with implicit objects auto-wiring, it makes the refactoring from quick prototyping zscript codes to product performance codes as simple as copy and paste.

We hope you enjoy these ZK new features and write your MVC codes happily with ZK. If you come up with any idea, please feel free to feed back and we can make ZK programming even easier and better.
分享到:
评论

相关推荐

    xf-a2010.64.zip

    1. "cpexym_70773.exe":这看起来像是一个可执行文件(.exe),可能是“xf-a2010”的安装程序。"cpexym"可能是软件的一部分代码名,而"70773"可能是版本号或者是内部编译的标识符。 2. "xf-a2010.exe":这是另一个可...

    xf-a2010注册机.rar

    xf-a2010注册机.rarx

    xf-adesk2012x64.rar

    最新的 autodesk2012 注册机 64位xf-adesk2012x64.rar

    xf-adsk2015_x64.exe

    xf-adsk2015_x64.exe inwentor註冊機

    xf-adsk2016_x64 CAD2016注册机

    xf-adsk2016_x64 CAD2016注册机

    xf-adobecc2015

    AE用于产生注册码的 xf-adobecc2015

    3D MAX2011注册机xf-a2011-64bits

    3D MAX 2011 注册机xf-a2011-64bits

    xf-mccs6.exe

    2. 打开注册机“xf-mccs6.exe”,点击“generate” 生成序列号; 3. 将生成的序列号前4位替换为:1424 1325-1627-6964-9232-5771-4755 1424-1627-6964-9232-5771-4755 4.安装flash builder 4.7软件 5、解压成功后,...

    XF-S4040中文语音合成芯片数据手册1.0

    ### XF-S4040中文语音合成芯片数据手册关键知识点解析 #### 一、概述 - **产品背景**:XF-S4040是科大讯飞推出的一款高性能中文语音合成芯片,旨在满足日益增长的语音交互需求。该芯片集成了先进的语音合成技术,...

    xf-adobecc

    2.打开 xf-adobecc.exe 注册机,生成序列号(请勿关闭注册机), 安装 ADOBE CC 软件: 点击“安装”(我已经购买), 点击登录 ADOBE ID,(请确保已经断网), 选择稍后连接, 接受许可协议,输入刚注册的序列号,...

    (XF - 2)ZK 入门

    【标题】:“(XF - 2) ZK 入门” 在IT行业中,ZK(Zul Kernel)是一个流行的开源Java轻量级Web组件库,主要用于构建富客户端用户界面。ZK提供了一种简单的方式来创建交互式的Web应用,无需处理JavaScript、Ajax或...

    xf-adsk2018_x64

    2018版AutoCAD加入了高分辨率4k支持,用户体验视觉效果会更好,但在网上找AutoCAD2018版的软件很难找,下载也非常慢,小编这里附上AutoCAD2018的64位简体中文破解版,还有注册,可以完美激活软件。

    语音合成模块-XF-S4240

    《科大讯飞语音合成模块XF-S4240详解》 科大讯飞作为国内领先的AI企业,其在语音技术领域拥有深厚的研发实力。XF-S4240语音合成模块便是该公司推出的一款高效能、易操作的文本转语音产品。这款模块能够将输入的文本...

    xf-adsk2016(x86/x64)注册机.rar

    软件介绍: AUTODESK 2016系列软件注册机-32及64位,网上转载过来的,仅供测试后删除。

    (XF - 8)ZK 宏的使用与定义Demo

    本主题“(XF - 8)ZK 宏的使用与定义Demo”着重讨论ZK框架中的宏定义及其应用。 ZK框架的核心理念是通过事件驱动和组件模型来简化Web开发,它提供了丰富的组件库,使得开发者可以像操作桌面应用一样操作Web界面。...

    xf-adesk2012x32.rar

    最新的 autodesk 2012 注册机 xf-adesk2012x32.rar

    Dahua大华DH-S3100C-24GT4XF-V2以太网交换机电汇聚和光汇聚快速操作手册.docx

    "Dahua大华DH-S3100C-24GT4XF-V2以太网交换机电汇聚和光汇聚快速操作手册" 本资源为Dahua大华DH-S3100C-24GT4XF-V2以太网交换机电汇聚和光汇聚快速操作手册,旨在提供正确的使用方法、安全须知、安装要求、维修要求...

    1-2-TL665xF-EasyEVM开发板硬件说明书.pdf

    广州创龙结合TI KeyStone系列多核架构TMS320C665x及Xilinx Artix-7系列FPGA设计的TL665xF-EasyEVM开发板是一款DSP+FPGA高速大数据采集处理平台,其底板采用沉金无铅工艺的6层板设计,适用于高端图像处理、软件无线电...

    xf86-video-intel-2.2.99.901

    《xf86-video-intel-2.2.99.901:为945/960集成显卡优化的Linux驱动详解》 在Linux操作系统中,硬件驱动是连接硬件设备与操作系统的重要桥梁,它负责管理和控制硬件设备的工作。针对Intel 945和960集成显卡,xf86-...

    xf86-video-qxl

    1. `configure.ac` 和 `Makefile.am`:这两个文件是自动化构建系统的一部分,通常用于GNU Autotools。`configure.ac`定义了编译时的检测条件和配置选项,而`Makefile.am`则定义了构建规则和目标,帮助开发者构建源...

Global site tag (gtag.js) - Google Analytics