Friday, September 20, 2013

ZK Create Helper Tag to Make Programmer Happier


Introduction

This article describe how to create helper tag by custom component to help programmer to do something easier/better.

NOTE:
This does not mean "this is a good way, follow this pattern"

Just show a possible way that you can give it a try and see if it makes the world better.

Result

Online demo:
http://screencast.com/t/gC2rGFg3KkuU

Program

index.zul

Contains a/button with very long url, also contains a/button/image/ with short url and specify parameters with custom helper component.

<zk>
    <!-- Tested with ZK 6.5.2
        test link available at wiki: http://en.wikipedia.org/wiki/Google_Chart_API
    -->

    <!-- basically you can write something as below,
        now assume that you need to
        find a typo
            or
        change some params...
     -->
    <div>
        <a href="http://chart.apis.google.com/chart?chs=200x200&amp;chdlp=b&amp;chtt=Uberman&amp;chdl=Asleep%7CAwake&amp;chd=t:1,11,1,11,1,11,1,11,1,11,1,11&amp;cht=p&amp;chco=586F8E%7C7D858F"
            target="blank">
            open chart
        </a>
    </div>
    <div>
        <button href="http://chart.apis.google.com/chart?chs=200x200&amp;chdlp=b&amp;chtt=Uberman&amp;chdl=Asleep%7CAwake&amp;chd=t:1,11,1,11,1,11,1,11,1,11,1,11&amp;cht=p&amp;chco=586F8E%7C7D858F"
            target="blank" label="open chart, too" />
    </div>
    <!-- or you can define a helper component to
        make those params clear as below
        to reduce server memory you can specify
        stubonly="true" to helper tags

        Of course this probably is not needed if
        you load those params from DB/Java-Beans and
        build link at server side

        Just show a possibility that such kind of helper
        can help you do some fast prototyping in zul page
        without server side (AP level) support

        And it is possible to change params dynamically so
        probably will be useful when using it with MVVM

        You can also define the helper tag as another component with
        another name in lang-addon.xml, see index_defined.zul
    -->
    <div>
        <a href="http://chart.apis.google.com/chart" target="blank">
            also open chart
            <div name="chs" value="200x200"
                stubonly="true" use="helper.Param" />
            <div name="chdlp" value="b"
                stubonly="true" use="helper.Param" />
            <div name="chtt" value="Uberman"
                stubonly="true" use="helper.Param" />
            <div name="chdl" value="Asleep%7CAwake"
                stubonly="true" use="helper.Param" />
            <div name="chd" value="t:1,11,1,11,1,11,1,11,1,11,1,11"
                stubonly="true" use="helper.Param" />
            <div name="cht" value="p"
                stubonly="true" use="helper.Param" />
            <div name="chco" value="586F8E%7C7D858F"
                stubonly="true" use="helper.Param" />
        </a>
    </div>
    <!-- for components that do not support child, you can
        use give it an ID and specify the ID as target of param
     -->
    <div>
        <button id="btn" label="still open chart" href="http://chart.apis.google.com/chart"
            target="blank" />
        <div name="chs" value="200x200"
            target="btn"
            stubonly="true" use="helper.Param" />
        <div name="chdlp" value="b"
            target="btn"
            stubonly="true" use="helper.Param" />
        <div name="chtt" value="Uberman"
            target="btn"
            stubonly="true" use="helper.Param" />
        <div name="chdl" value="Asleep%7CAwake"
            target="btn"
            stubonly="true" use="helper.Param" />
        <div name="chd" value="t:1,11,1,11,1,11,1,11,1,11,1,11"
            target="btn"
            stubonly="true" use="helper.Param" />
        <div name="cht" value="p"
            target="btn"
            stubonly="true" use="helper.Param" />
        <div name="chco" value="586F8E%7C7D858F"
            target="btn"
            stubonly="true" use="helper.Param" />
    </div>
    <div>
        Display an image
        <image id="img" src="http://chart.apis.google.com/chart" />
        <div name="chs" value="200x200"
            target="img"
            stubonly="true" use="helper.Param" />
        <div name="chdlp" value="b"
            target="img"
            stubonly="true" use="helper.Param" />
        <div name="chtt" value="Uberman"
            target="img"
            stubonly="true" use="helper.Param" />
        <div name="chdl" value="Asleep%7CAwake"
            target="img"
            stubonly="true" use="helper.Param" />
        <div name="chd" value="t:1,11,1,11,1,11,1,11,1,11,1,11"
            target="img"
            id="dynaParam"
            use="helper.Param" />
        <div name="cht" value="p"
            target="img"
            stubonly="true" use="helper.Param" />
        <div name="chco" value="586F8E%7C7D858F"
            target="img"
            stubonly="true" use="helper.Param" />
        and change it dynamically
        <combobox onSelect="dynaParam.setValue(self.getValue());">
            <comboitem label="t:1,11,1,11,1,11,1,11,1,11,1,11" />
            <comboitem label="t:2,10,2,10,2,10,2,10,2,10,2,10" />
        </combobox>
    </div>
</zk>


index_defined.zul

Similar to index.zul, just use the custom component with another name defined in lang-addon.xml.

<zk>
    <!-- test link available at wiki: http://en.wikipedia.org/wiki/Google_Chart_API -->

    <!-- Do the same as index.zul with
        predefined param component.
     -->
    <div>
        <a href="http://chart.apis.google.com/chart?chs=200x200&amp;chdlp=b&amp;chtt=Uberman&amp;chdl=Asleep%7CAwake&amp;chd=t:1,11,1,11,1,11,1,11,1,11,1,11&amp;cht=p&amp;chco=586F8E%7C7D858F"
            target="blank">
            open chart
        </a>
    </div>
    <div>
        <button href="http://chart.apis.google.com/chart?chs=200x200&amp;chdlp=b&amp;chtt=Uberman&amp;chdl=Asleep%7CAwake&amp;chd=t:1,11,1,11,1,11,1,11,1,11,1,11&amp;cht=p&amp;chco=586F8E%7C7D858F"
            target="blank" label="open chart, too" />
    </div>
    <div>
        <a href="http://chart.apis.google.com/chart" target="blank">
            also open chart
            <param name="chs" value="200x200"
                stubonly="true" />
            <param name="chdlp" value="b"
                stubonly="true" />
            <param name="chtt" value="Uberman"
                stubonly="true" />
            <param name="chdl" value="Asleep%7CAwake"
                stubonly="true" />
            <param name="chd" value="t:1,11,1,11,1,11,1,11,1,11,1,11"
                stubonly="true" />
            <param name="cht" value="p"
                stubonly="true" />
            <param name="chco" value="586F8E%7C7D858F"
                stubonly="true" />
        </a>
    </div>
    <div>
        <button id="btn" label="still open chart" href="http://chart.apis.google.com/chart"
            target="blank" />
        <param name="chs" value="200x200"
            target="btn"
            stubonly="true" />
        <param name="chdlp" value="b"
            target="btn"
            stubonly="true" />
        <param name="chtt" value="Uberman"
            target="btn"
            stubonly="true" />
        <param name="chdl" value="Asleep%7CAwake"
            target="btn"
            stubonly="true" />
        <param name="chd" value="t:1,11,1,11,1,11,1,11,1,11,1,11"
            target="btn"
            stubonly="true" />
        <param name="cht" value="p"
            target="btn"
            stubonly="true" />
        <param name="chco" value="586F8E%7C7D858F"
            target="btn"
            stubonly="true" />
    </div>
    <div>
        Display an image
        <image id="img" src="http://chart.apis.google.com/chart" />
        <param name="chs" value="200x200"
            target="img"
            stubonly="true" />
        <param name="chdlp" value="b"
            target="img"
            stubonly="true" />
        <param name="chtt" value="Uberman"
            target="img"
            stubonly="true" />
        <param name="chdl" value="Asleep%7CAwake"
            target="img"
            stubonly="true" />
        <param name="chd" value="t:1,11,1,11,1,11,1,11,1,11,1,11"
            target="img"
            id="dynaParam"
            use="helper.Param" />
        <param name="cht" value="p"
            target="img"
            stubonly="true" />
        <param name="chco" value="586F8E%7C7D858F"
            target="img"
            stubonly="true" />
        and change it dynamically
        <combobox onSelect="dynaParam.setValue(self.getValue());">
            <comboitem label="t:1,11,1,11,1,11,1,11,1,11,1,11" />
            <comboitem label="t:2,10,2,10,2,10,2,10,2,10,2,10" />
        </combobox>
    </div>
</zk>


Param.java

The implementation of custom helper component,

package helper;

import org.zkoss.zk.ui.Component;
import org.zkoss.zk.ui.event.Event;
import org.zkoss.zk.ui.event.EventListener;
import org.zkoss.zk.ui.event.Events;
import org.zkoss.zul.A;
import org.zkoss.zul.Button;
import org.zkoss.zul.Div;
import org.zkoss.zul.Image;

/** Tested with ZK 6.5.2
 * 
 * @author benbai123
 *
 */
public class Param extends Div {

    private static final long serialVersionUID = 8842930664701238051L;

    private String _name = "";
    private String _value = "";
    private String _target;
    private String _oldValue = "";

    @SuppressWarnings({ "unchecked", "rawtypes" })
    public Param () {
        // update target url while created
        addEventListener(Events.ON_CREATE, new EventListener () {
            public void onEvent (Event event) {
                updateTargetUrl();
            }
        });
        // do not output any html
        setWidgetOverride ("redraw", "function (out) {}");
    }
    // setters
    public void setTarget (String target) {
        _target = target;
    }
    public void setName (String name) {
        if (name == null) {
            name = "";
        }
        _name = name;
    }
    public void setValue (String value) {
        if (value == null) {
            value = "";
        }
        _value = value;
        updateTargetUrl();
    }

    private void updateTargetUrl () {
        if (!_name.isEmpty() && !_value.isEmpty()) {
            Component target = findTarget();
            if (target instanceof A) {
                A link = (A)target;
                String url = updateUrl(link.getHref());
                link.setHref(url);
            } else if (target instanceof Button) {
                Button btn = (Button)target;
                String url = updateUrl(btn.getHref());
                btn.setHref(url);
            } else if (target instanceof Image) {
                Image img = (Image)target;
                String url = updateUrl(img.getSrc());
                img.setSrc(url);
            }
        }
    }
    // try to find target to update
    private Component findTarget () {
        if (_target != null && !_target.isEmpty()) {
            return getParent().getFellowIfAny(_target);
        } else if (getParent() instanceof A) {
            return getParent();
        }
        return null;
    }
    // update url with param/value
    private String updateUrl (String url) {
        String value;
        if (url.indexOf("?") == -1) {
            url += "?";
        } else {
            url += "&";
        }
        value = _name + "=" + _value;
        // replace old value or append new value at tail
        if (!_oldValue.isEmpty()) {
            url.replace(_oldValue, value);
        } else {
            url += value;
        }
        return url;
    }
}


lang-addon.xml

Define 'param' component.

<!-- Tested with ZK 6.5.2
    Define param component
-->

<language-addon>
    <addon-name>param</addon-name>
    <language-name>xul/html</language-name>

    <!-- It specifies what language addons this addon
        depends on. If specified, this addon will be
        parsed after all the specified addons are parsed -->
    <depends>zul</depends>

    <!-- define param -->
    <component>
        <component-name>param</component-name>
        <!-- extends div and change java class -->
        <extends>div</extends>
        <component-class>helper.Param</component-class>
    </component>
</language-addon>


zk.xml

Load lang-addon.xml.

<zk>
    <!-- load lang-addon.xml -->
    <language-config>
        <addon-uri>/WEB-INF/lang-addon.xml</addon-uri>
    </language-config>
</zk>


References

Language Definition
http://books.zkoss.org/wiki/ZK_Client-side_Reference/Language_Definition

The language-config Element
http://books.zkoss.org/wiki/ZK_Configuration_Reference/zk.xml/The_language-config_Element

Download

Full project at github
https://github.com/benbai123/ZK_Practice/tree/master/Components/projects/Components_Customization/ParamTagForUrl

Demo Flash

No comments:

Post a Comment