エンタープライズギークス (Enterprise Geeks)

企業システムの企画・開発に携わる技術者集団のブログです。開発言語やフレームワークなどアプリケーション開発に関する各種情報を発信しています。ウルシステムズのエンジニア有志が運営しています。

GWTの拡張(1) - ウェイティングパネル

GWT(Google Web Toolkit)でWaitingPanel(ウエイティングパネル)を作成する方法を紹介する。

GWTは、サーバ通信にGWT-RPC(GWTリモート・プロシージャ・コール)を使用している。GWT-RPCではAjaxを利用して非同期通信を行うため、リクエストをサーバへ送信後、レスポンスを待たずに他の操作が可能である。
たとえば注文登録時に、郵便番号を入力した場合、画面を切替えずに住所が画面に表示されるだけでなく、住所の表示を待たずに次の項目を入力できる。これが非同期の便利な部分である。

しかし、注文確定のような場合、サーバー処理が完結するまで他の操作をできないようにしたい。Webアプリケーションでは当たり前の、この同期処理をGWT-RPCはサポートしていない。

そこで必要となるのがWaitingPanelである。
以下に示すのは、サーバーからの応答を待つ間、ユーザーが操作が出来ないようにブラウザ全体を覆い尽くすガラス窓のようなパネルを表示するコードである。

import com.google.gwt.dom.client.Style.BorderStyle;
import com.google.gwt.dom.client.Style.Position;
import com.google.gwt.dom.client.Style.Unit;
import com.google.gwt.event.logical.shared.ResizeEvent;
import com.google.gwt.event.logical.shared.ResizeHandler;
import com.google.gwt.user.client.DOM;
import com.google.gwt.user.client.Element;
import com.google.gwt.user.client.ui.Label;
import com.google.gwt.user.client.ui.PopupPanel;
import com.google.gwt.user.client.ui.RootPanel;
import com.google.gwt.user.client.ui.Widget;

public class WaitingPanel extends PopupPanel implements ResizeHandler {

    interface PanelPositionSetter {
        void setPosition(Element element);
    }

    private PanelPositionSetter positionSetter;

    public WaitingPanel() {
        this(new Label("Waiting..."));
    }

    public WaitingPanel(final Widget widget) {
        super(false, true);
        initialize(widget, createPositionSetter());
    }

    @Override
    public void onResize(final ResizeEvent event) {
        this.positionSetter.setPosition(this.getWidget().getElement());
    }

    public void show() {
        super.show();
        this.positionSetter.setPosition(this.getWidget().getElement());
        DOM.setStyleAttribute(RootPanel.get().getElement(), "cursor",
                "waiting");
    }

    public void hide() {
        super.hide();
        DOM.setStyleAttribute(RootPanel.get().getElement(), "cursor", "");
    }

    protected void initialize(final Widget child,
            final PanelPositionSetter position) {
        getElement().getStyle().setZIndex(999998);
        getElement().getStyle().setOpacity(0.8);
        getElement().getStyle().setBackgroundColor("#555555");
        getElement().getStyle().setWidth(100, Unit.PCT);
        getElement().getStyle().setHeight(100, Unit.PCT);
        getElement().getStyle().setBorderStyle(BorderStyle.NONE);
        child.getElement().getStyle().setZIndex(999999);
        this.setWidget(child);

        this.positionSetter = position;
    }

    protected PanelPositionSetter createPositionSetter() {
        return new PanelPositionSetter() {
            @Override
            public void setPosition(final Element element) {
                element.getStyle().setPosition(Position.ABSOLUTE);
                element.getStyle().setTop(50, Unit.PCT);
                element.getStyle().setLeft(50, Unit.PCT);
            }
        };
    }
}

これを利用するには、同期処理させたいイベントの先頭でWaitingPanelをshowし、確定したらhideする。

public class Example ... {
...
    public void setAddress(final String zipCode) {
        final WaitingPanel waiting = new WaitingPanel();
        waiting.show();

        MyRemoteServiceAsync service = GWT.create(MyRemoteService.class);
        service.getAddress(zipCode,
                new AsyncCallback<String>() {

                    @Override
                    public void onSuccess(final String result) {
                        // implements ...
                        waiting.hide();
                    }

                    @Override
                    public void onFailure(Throwable caught) {
                        waiting.hide();
                        // Error handling ...
                    }
                });
    }
}

[高橋 友樹]