Thursday, June 27, 2013

ZK Decimalbox Support Scientific Notation


Introduction

Currently ZK Decimalbox does not support scientific notation, this article describe how to customize it to support scientific notation.

Result

View demo online
http://screencast.com/t/ZtbTj0H2M

Pre-request

Extending and Customizing ZK Component as Separate Component
http://ben-bai.blogspot.tw/2013/01/extending-and-customizing-zk-component.html

Program

support_scientific_notation.zul

<zk>
    decimalbox:
    <decimalbox constraint="no empty"
        textAlign="right">
        <attribute name="onChange"><![CDATA[
            lb.setValue(self.getValue() + "");
            lbTwo.setValue(self.getValue().intValue() + "");
        ]]></attribute>
        <attribute name="onScientificNotation"><![CDATA[
            lb.setValue(self.getValue() + "");
            lbTwo.setValue(self.getValue().intValue() + "");
        ]]></attribute>
    </decimalbox>
    <div />
    BigDecimal value: <label id="lb" />
    <div />
    int value: <label id="lbTwo" />
    <div style="margin-top: 30px;" />
    rounded decimalbox
    <decimalbox mold="rounded" constraint="no empty"
        textAlign="right">
        <attribute name="onChange"><![CDATA[
            lbThree.setValue(self.getValue() + "");
            lbFour.setValue(self.getValue().intValue() + "");
        ]]></attribute>
        <attribute name="onScientificNotation"><![CDATA[
            lbThree.setValue(self.getValue() + "");
            lbFour.setValue(self.getValue().intValue() + "");
        ]]></attribute>
    </decimalbox>
    <div />
    BigDecimal value: <label id="lbThree" />
    <div />
    int value: <label id="lbFour" />
</zk>


Decimalbox.java

Custom java class for decimalbox, support scientific notation, also support text align attribute.

package test.components;

import java.math.BigDecimal;
import java.util.Map;

import org.zkoss.zk.au.out.AuInvoke;
import org.zkoss.zk.ui.event.Events;
import org.zkoss.zk.ui.event.InputEvent;
import org.zkoss.zk.ui.util.Clients;


/** Tested with ZK 6.0.2
 * 
 * @author benbai123
 *
 */
public class Decimalbox extends org.zkoss.zul.Decimalbox {

    private String _textAlign = "left";
    private static final long serialVersionUID = -4797186118579295417L;
    static {
        // listen custom event
        addClientEvent(Decimalbox.class, "onCheckErrorValue", CE_IMPORTANT | CE_DUPLICATE_IGNORE | CE_NON_DEFERRABLE);
    }
    public void setTextAlign (String textAlign) {
        // update if there is a different value
        if (textAlign != null // no null
            && !textAlign.isEmpty() // no empty
            && !textAlign.equals(_textAlign)) { // value is changed
            _textAlign = textAlign;
            smartUpdate("textAlign", _textAlign);
        }
    }
    public String getTextAlign () {
        return _textAlign;
    }

    // override
    // process client event
    public void service(org.zkoss.zk.au.AuRequest request, boolean everError) {
        final String cmd = request.getCommand();
        // process custom event
        if (cmd.equals("onCheckErrorValue")) {
            // get data map
            Map<String, Object> data = request.getData();
            // get value
            String value = (String)data.get("value");
            boolean valid = true;
            BigDecimal val = null;
            // try create a BigDecimal with the wrong value
            try {
                val = new BigDecimal(value);
            } catch (Exception e) {
                // failed to create BigDecimal, not valid
                valid = false;
            }
            if (valid) {
                // it is valid for BigDecimal
                setValueDirectly(val);
                // post onScientificNotation event
                Events.postEvent("onScientificNotation", this, null);
                // clear the error status if any
                clearErrorMessage();
                Clients.clearWrongValue(this);
            } else {
                // it is invalid for BigDecimal
                // really do _markError at client side
                // the code below will call function _realMarkError
                // of the widget class at client side 
                response("DecimalboxSupportScientificNotation", new AuInvoke(this, "_realMarkError", (String[])null));
            }
        } else 
            super.service(request, everError);
    }
    protected void renderProperties(org.zkoss.zk.ui.sys.ContentRenderer renderer)
        throws java.io.IOException {
        super.renderProperties(renderer);
        if (!"left".equals(_textAlign)) {
            render(renderer, "textAlign", _textAlign);
        }
    }
}


Decimalbox.js

Custom widget class for decimalbox, will ask server at first while do _maskError.

test.components.Decimalbox = zk.$extends(zul.inp.Decimalbox, {
    _textAlign: 'left',
    setTextAlign: function (v) {
        if (v != this._textAlign) {
            this._textAlign = v;
            var inp = this.getInputNode();
            if (inp)
                jq(inp).css('text-align', this._textAlign);
        }
    },
    bind_: function () {
        this.$supers('bind_', arguments);
        jq(this.getInputNode()).css('text-align', this._textAlign);
    },
    getAllowedKeys_: function () {
        // add 'E' into allowed keys
        return this.$supers('getAllowedKeys_', arguments) + 'E';
    },
    _markError: function (msg, val, noOnError, doDirectly) {
        if (doDirectly) {
            // do it without server confirmation
            this.$supers('_markError', arguments);
        } else {
            // store info only, waiting for server confirmation
            this._markErrorInfo = {msg: msg, val: val, noOnError: noOnError};
            // ask server side custom validation
            var wgt = this,
                timer = this._onCheckErrorValueTimer;
            // prevent duplicated event
            if (timer)
                clearTimeout(timer);
            this._onCheckErrorValueTimer = setTimeout (function () {
                wgt.fire('onCheckErrorValue', {value: jq(wgt.getInputNode()).val()});
            }, 300);
        }
    },
    // called by server
    // do _markError directly with the stored information
    _realMarkError: function () {
        var info = this._markErrorInfo;
        if (info) {
            this._markError(info.msg, info.val, info.noOnError, true);
            this._markErrorInfo = null;
        }
    }
});


zk.xml

Specify lang-addon.xml

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


lang-addon.xml

Define the customized decimalbox

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

    <!-- extends ZK Decimalbox and use custom classes -->
    <component>
        <component-name>decimalbox</component-name>
        <component-class>test.components.Decimalbox</component-class>
        <widget-class>test.components.Decimalbox</widget-class>
        <mold>
            <mold-name>default</mold-name>
        </mold>        
        <mold>
            <mold-name>rounded</mold-name>
        </mold>
    </component>
</language-addon>


zk.wpd

Used to load widget class.

<package name="test.components" language="xul/html" depends="zul.inp">
    <widget name="Decimalbox" />
</package>


References

InputWidget.js
https://github.com/zkoss/zk/blob/master/zul/src/archive/web/js/zul/inp/InputWidget.js

InputElement.java
https://github.com/zkoss/zk/blob/master/zul/src/org/zkoss/zul/impl/InputElement.java

Download

1 comment:

  1. how to work with focus?http://stackoverflow.com/questions/17424124/zk-why-zk-drop-getfocus-or-isfocus

    ReplyDelete