GXT, GWT Ext - Safe call of the "after render" kind of functions

Problem:

How many times did you have problems with calling a widget function (after-rendering), which is working only after the widget was rendered?

Solution:

The main idea is to delay the execution of the function after the widget was rendered. How we can do this? Very simple, just adding a handler function on render event. Here's presented the solution source code for the best 3 libraries:
  • using GXT framework
public interface Function {
    public void execute ();
}

/**
* Safe function call on a component, which was rendered or not.
*
* @param c Component object that must be not null.
* @param f Function object with the function that must be called.
*/
public static void safeFunctionCallOn(final Component c, final Function f) {
    c.enableEvents(true);
    if (c.isRendered()) {
        f.execute();
    } else {
        final Listener lsnr = new Listener() {
            @Override
            public void handleEvent(final ComponentEvent be) {
                f.execute();
            }
        };
        c.addListener(Events.Render, lsnr);
    }
}

  • using GWT-Ext framework
For this library we don't have to define the "Function" interface, like in the above code, because it's alredy defined in the GWT-Ext jar:

/**
* Safe function call on a component, which was rendered or not.
*
* @param c Component object that must be not null.
* @param f Function object with the function that must be called.
*/
public static void safeFunctionCallOn(final Component c, final Function f) {
    if (c.isRendered()) {
         f.execute();
    } else {
        c.addListener("render", f);
    }
}

  • using SmartGWT framework:
Also, for this library we don't need to define the interface "Function", because it already exists in the SmartGWT jar.

/**
* Safe function call on a canvas, which was rendered or not.
*
* @param c Canvas object that must be not null.
*
* @param f Function object with the function that must be called.
*/
public static void safeFunctionCallOn(final Canvas c, final Function f) {
    if (c.isDrawn()) {
        f.execute();
    } else {
        c.doOnRender(f);
    }
}

Note: the static function "safeFunctionCallOn" is safe to be called for either non-rendered or rendered widget, because its execution will always be deferred after the widget was rendered !

The call of this function is very simple, like here:

final Component panel = ...
safeFunctionCallOn (panel, new Function() {
    @Override
    public void execute() {
         /* after-rendering function */
         panel.setSomethingFunction (); 
    }
});

There is the other type of functions (before-rendering), which must be called before the widget was rendered. You'll have an error at runtime if you call the function after the widget was rendered. So, for solving this, you have to move the call of the function before the widget was added to a container.

Cheers!

8 comments:

  1. Thanks for posting this. It is a common usecase. Note that in the case of SmartGWT the use can also register a addDrawHandler(..) but you're right, the convenience doOnRender(Function) was added precisely for this.

    ReplyDelete
  2. Thanks for the comment, Sanjiv.

    I have a question, though, why did you call "RenderHandler" in "DrawHandler" ?

    since programmers aren't used with this new terminology, it would be harder for them to adopt your new terms regrading these simple things. In other words, it would be easier for people to understand and use your framework, if they use already known terms.

    Cheers!

    ReplyDelete
  3. Canvas is the base class of SmartGWT widgets and throughout the library there is reference to widgets being drawn, and Canvas.draw() is one of the key methods. Hence the listener is addDrawHandler().

    public interface HasDrawHandlers extends HasHoverHandlers {
    HandlerRegistration addDrawHandler(DrawHandler handler);
    }

    public interface DrawHandler extends EventHandler {
    void onDraw(DrawEvent event);
    }

    But you're right in that there's a little naming inconsistency in having a doOnRender(..) method. I'll look into this.

    ReplyDelete
  4. I use same approach but I do annotate all such methods with @OnRender. Via deffered binding such methods turns into methods that executes same way as you decribed. Now or only after rendered.

    Kinda tricky, huh?

    ReplyDelete
  5. Cool, I know that annotations are supported by GWT, but I never used them, so far :(
    Can you give me a simple example, please?

    Thanks!

    ReplyDelete
  6. Nothing special. You knew about Generators?

    ReplyDelete
  7. >public interface Function (){
    hm.. is this java language ?

    ReplyDelete
  8. You're right Dmitry, I corrected it ... I was too fast in typing -- and this is happening when you write and don't read after that ;-))

    Cheers!

    ReplyDelete