PlaceManager
※以下は多分に想像も含まれるので注意。
モチベーション
生のGWTを使用したアプリでは、(サードパーティ製のライブラリを使わない限り)ただ一つのhtml上にすべてを描画することになる。 しかし、当然ながら「ページ」を切り替えながらユーザとの対話を行いたいアプリの作成は面倒になる。
例えばDeckLayoutPanelに複数のページウィジェットを重ねておき、見せたいものを指示するという方法がある(つまり紙芝居方式)。 ところが、この方式ではおそらく、
- すべてのページウィジェットを最初に生成しなくてはならない。
- ブラウザのバックボタン等に追従しない。
という問題がある。GWTPのPlaceManagerはこれを行うものであり、その機能としては
- Proxyを使うことにより、ページの生成はオンデマンドで行われる(おそらくプロキシオブジェクトだけが最初にすべて生成される)。
- ブラウザのバック・フォワードボタンをハンドリングして、適切なページを表示する。
というものと思われる。つまり、PlaceManagerの扱う「ページ」をPlaceと称している。
利用方法
GWTPの自動生成するサンプルコードはDIのせいで流れがわかりにくくなっているが、おおまかな流れとしては以下であると思われる。
Placeの名称を決める
各ページ(Place)の名称を決定する。サンプルコードでは、NameTokensクラスの"main"と"responce"である。
public class NameTokens { public static final String main = "main"; public static final String response = "response"; ....
PlaceManagerを作成する
このアプリ専用のPlaceManagerを作成する。これはClientPlaceManagerというクラスになる。 Placeへの移動要求はPlaceRequestオブジェクトで行うのだが、デフォルトのPlace用のリクエストを作成しておく。 以下のコードでは、GInjectorを使って、デフォルトPlaceの名称が注入されるようになっている。
public class ClientPlaceManager extends PlaceManagerImpl { private final PlaceRequest defaultPlaceRequest; @Inject public ClientPlaceManager(final EventBus eventBus, final TokenFormatter tokenFormatter, @DefaultPlace final String defaultPlaceNameToken) { super(eventBus, tokenFormatter); this.defaultPlaceRequest = new PlaceRequest(defaultPlaceNameToken); } @Override public void revealDefaultPlace() { revealPlace(defaultPlaceRequest, false); } }
Placeの実装を作成する
Placeの名称とそれを管理するオブジェクトを作成したので、次にPlaceの実装を作成する。 この実体は、Presenter(MVPのP)オブジェクトになるのだが、このオブジェクトは非常に大きくなる可能性があるため、 実際には、そのプロキシを作成してPlaceの名称をつけておく。"main"については、MainPagePresenterクラスの内部クラスであるMyProxyになる。
... @ProxyStandard @NameToken(NameTokens.main) public interface MyProxy extends ProxyPlace<MainPagePresenter> { } ...
"response"についても同じ、ResponsePresenterクラスのMyProxyになる。
... @ProxyStandard @NameToken(NameTokens.response) public interface MyProxy extends ProxyPlace<ResponsePresenter> { } ...
@ProxyStandardについては詳細不明。通常はこれをつけておけばよいようだ。
ワイヤリングを行う
gwtpでは標準でGINを使用しているので、このワイヤリングを行う。
ClientModule.java
.... public class ClientModule extends AbstractPresenterModule { @Override protected void configure() { install(new DefaultModule(ClientPlaceManager.class)); bindPresenter(MainPagePresenter.class, MainPagePresenter.MyView.class, MainPageView.class, MainPagePresenter.MyProxy.class); bindConstant().annotatedWith(DefaultPlace.class).to(NameTokens.main); bindPresenter(ResponsePresenter.class, ResponsePresenter.MyView.class, ResponseView.class, ResponsePresenter.MyProxy.class); } }
ClientGinjector.java
... @GinModules({ DispatchAsyncModule.class, ClientModule.class }) public interface ClientGinjector extends Ginjector { EventBus getEventBus(); PlaceManager getPlaceManager(); Provider<MainPagePresenter> getMainPagePresenter(); Provider<ResponsePresenter> getResponsePresenter(); }