Sunday, September 9, 2012

ZK: Adding abort button to busy mask



Introduction

In ZK, we can use a mask to prevent any action during a long operation, sometimes we may want to provide 'abort' function to terminate the long operation.

In this case, we can use Client Side Programming to add some widget (with respect to the abort function) into the mask instead of recreating or custommizing the UI completely.

This post will show how to add an abort button into the 'busy' mask to provide the 'abort' function to terminate a long operation.

The Program

adding_abort_button_to_busy_mask.zul

<zk>
    <script type="text/javascript"><![CDATA[
        function showBusy () {
            // show busy mask
            zAu.cmd0.showBusy('Loading...');
            // move abort button under busy message
            jq('.z-loading')[0].appendChild(jq('$abortButton')[0]);
        }
        function clearBusy () {
            // move abort button back under abort div
            jq('$abortDiv')[0].appendChild(jq('$abortButton')[0]);
            // clear busy mask
            zAu.cmd0.clearBusy(null);
        }
    ]]></script>
    <zscript><![CDATA[
        /** A class that implement Runnable to do
         * the long operation
         */
        class AbortableRunnable implements Runnable {
            boolean stopped = true;
            int i = 0;
            public void run () {
                stopped = false;
                while (true) {
                    // do somoething
                    i++;
                    try {
                        Thread.sleep(1000);
                    } catch (Exception e) {
                        System.out.println(e);
                    }
                    // finish or aborted
                    if (i == 5 || stopped)
                        break;
                }
            }
            public void abort () {
                stopped = true;
            }
            public boolean isRunning () {
                return !stopped;
            }
            public int getI () {
                return i;
            }
        }
        AbortableRunnable ar = new AbortableRunnable();
        
        void start () {
            // start thread
            new Thread(ar).start();
        }
        void abort () {
            // update status
            status.setValue("aborted i = " + ar.getI());
            // stop thread
            ar.abort();
            // reset
            ar = new AbortableRunnable();
        }
        void finish () {
            // update status
            status.setValue("finished i = " + ar.getI());
            // reset
            ar = new AbortableRunnable();
        }
    ]]></zscript>
    <!-- abort div to keep the abort button,
        display outside the screen -->
    <div id="abortDiv" style="position: absolute; left: -1000px;">
        <!-- abort button stop the running thread any time
            and clear busy mask -->
        <button id="abortButton" label="abort">
            <attribute name="onClick">
                // abort the running process
                abort();
                // stop the checking timer
                checkTimer.stop();
                // move self element back to abort div
                // and clear the busy mask
                Clients.evalJavaScript("clearBusy();");
            </attribute>
        </button>
    </div>
    <div style="margin: 50px;">
        <!-- start button atart the thread
            and show busy mask -->
        <button label="do something long">
            <attribute name="onClick">
                // start to run the process
                start();
                // start the checking timer
                checkTimer.start();
                // show busy mask and move
                // the element of abort button under busy message
                Clients.evalJavaScript("showBusy();");
            </attribute>
        </button>
        <!-- checking timer clear busy mask after thread finished successfully -->
        <timer id="checkTimer" running="false" repeats="true" delay="100">
            <attribute name="onTimer">
                if (ar.isRunning()) // update status if is running
                    status.setValue("running... i = " + ar.getI());
                if (ar.getI() == 5) { // finish condition
                    finish();
                    // stop self
                    self.stop();
                    // move self element back to abort div
                    // and clear the busy mask
                    Clients.evalJavaScript("clearBusy();");
                }
            </attribute>
        </timer>
        Status: <label id="status" value="" />
    </div>
</zk>


The Result

View the demo flash on line
http://screencast.com/t/jIQV4hr7HlJy

Reference
http://ben-bai.blogspot.tw/2012/07/introduction-sometimes-we-may-need-to.html

Download

Source code at github
https://github.com/benbai123/ZK_Practice/blob/master/Components/projects/Components_Practice/WebContent/adding_abort_button_to_busy_mask.zul

Demo flash at github
https://github.com/benbai123/ZK_Practice/blob/master/Components/demos/adding_abort_button_to_busy_mask.swf

17 comments:

  1. I will want to know one thing as Zkprovide the busy mask small circle when some processing going on can it possible we can show that in middle of the page and when this busy circle will display the background page will not clickable it show light white the background page.

    ReplyDelete
    Replies
    1. You can mask page manually, please refer to http://ben-bai.blogspot.tw/2012/07/introduction-sometimes-we-may-need-to.html

      Delete
    2. But i will want use a common thing which can be apply over whole application in a common place and when ever i am clicking any button it automatically appear user will not able to click any other thing whenever request not completed.

      Delete
    3. You can try override zk.procDelay, zk.startProcessing and zk.endProcessing, please refer to the sample at zkfiddle (http://zkfiddle.org/sample/2clefv0/1-Handle-processing-effect)

      Delete
  2. Hi Ben ,
    As per your example i override ZK JS for showing the Processing Widnow over any page when any Ajax Request is going on but i am getting one issue in maney ZUL pages i am not able to entere data into textbox from the time when i added this functionality do you know what can be issue ?

    ReplyDelete
    Replies
    1. Is that textbox listening to onChanging event? Maybe you can try to increase zk.procDelay = 0; while typing

      Delete
    2. This comment has been removed by a blog administrator.

      Delete
    3. Sorry I accidently removed the latest reply, the '5' means 5 milliseconds, what if you change it to 5000? Is it working correctly if you remove all the override? Is there any runnable can reproduce this issue?

      Delete
    4. Its ok...Actully if i removed the override JS then it is working fine only when i am overriding JS then it is creating issue..i have added zk.procDelay = 5000 even then it is not working when i removed the onFocus="@command('clearDefaultCaseNumber')" then only it worked .So Busy Masking will not working with OnChange,onFocus etc. case?

      Delete
    5. I cannot reproduce this issue, it works well for me, please refer to the updated sample

      http://zkfiddle.org/sample/2clefv0/3-Handle-processing-effect

      Delete
    6. See i have updated your example with onFocus i am able to reproduce the issue
      http://zkfiddle.org/sample/2clefv0/4-Handle-processing-effect-With-OnFocus

      Delete
    7. Found the problem, you can try override doFocus_ with a flag

      http://zkfiddle.org/sample/2clefv0/7-Handle-processing-effect-with-flag

      or use timer

      http://zkfiddle.org/sample/2clefv0/8-Handle-processing-effect-with-timeout

      Delete
    8. Found a much easier way:

      http://zkfiddle.org/sample/2clefv0/9-Handle-processing-effect

      Delete
  3. Thanks looking better than previous

    ReplyDelete
  4. Replies
    1. it is a javascript object contains several functions, just a name.

      Delete