読者です 読者をやめる 読者になる 読者になる

GWTサンプル: History

先日のGoogleDeveloperDay2010のGWTのセッションにもあったようにに、最近のAjaxアプリケーションでもブラウザの戻る/進むボタンへの対応や、ブックマークフレンドリー、サーチエンジンがクロール可能であること、というのが求められていると思います。

例えばGmailでは、戻るボタンをクリックするとページ遷移せずにひとつ前の状態に戻りますし(inboxからメールを開いた状態で戻るをクリックするとinboxに戻ります)、個別のメールや検索結果を表示している状態でのURLはブックマーク可能になっています。GWTでもこのような履歴の処理を実装することができます。

GWTでの履歴はURLに付加されたトークンで表されます(例えばGmalのURLの#inboxの部分)。

https://mail.google.com/mail/#inbox

ユーザーが操作を行うたびに、スタックにトークンが追加されていきます。ただし、GWT自体でのサポートは現状ではプリミティブなもので、スタックへのトークンの追加や戻るボタンがクリックされた際の処理などは自分で実装する必要があります。

このエントリはこれらの処理についてのサンプルです。動作するサンプルはここにあります。主要な部分のソースは以下のとおりです。

	public void onModuleLoad() {

		VerticalPanel vPanel = new VerticalPanel();
		vPanel.setSpacing(5);
		vPanel.setHorizontalAlignment(VerticalPanel.ALIGN_CENTER);

		tab = new DecoratedTabPanel();
		vPanel.add(tab);

		// タブを5つ生成して履歴管理の対象とする
		for(int i=0; i<5; i++){
			String name = "Tab " + i;
			tab.add(new HTML(name), name);

			vPanel.add(new Hyperlink(name, TOKEN_TAB + i));
		}
		tab.selectTab(0);
		tab.setWidth("500");
		tab.addSelectionHandler(this);

		tab.add(new HTML("タブのタイトルにHyperlinkを入れるテスト"), new Hyperlink("Tab 5", "tab-5"));

		// ボタン
		HorizontalPanel buttonPanel = new HorizontalPanel();
		buttonPanel.setSpacing(3);
		buttonPanel.add(new Button("戻る", new ClickHandler() {
			@Override
			public void onClick(ClickEvent event) {
				addStatus("Back button clicked!");
				History.back();
			}
		}));
		buttonPanel.add(new Button("進む", new ClickHandler() {
			@Override
			public void onClick(ClickEvent event) {
				addStatus("Forward button clicked!");
				History.forward();
			}
		}));
		vPanel.add(buttonPanel);

		// ステータス表示用パネル
		statusPanel = new ScrollPanel(new HTML());
		statusPanel.setStyleName("statusPanel");
		vPanel.add(statusPanel);

		RootPanel.get("main").add(vPanel);

		History.addValueChangeHandler(this);

		// 初期状態での履歴トークンを処理する
		if(!History.getToken().isEmpty()){
			History.fireCurrentHistoryState();
		}
	}

	@Override
	public void onSelection(SelectionEvent<Integer> event) {
		// タブが選択されたら履歴に追加するがイベントは発生させない
		History.newItem(TOKEN_TAB + event.getSelectedItem(), false);
	}

	@Override
	public void onValueChange(ValueChangeEvent<String> event) {
		if(event.getValue().startsWith(TOKEN_TAB)){
			int selected = Integer.valueOf(event.getValue().substring(event.getValue().indexOf(TOKEN_TAB) + 4));
			tab.selectTab(selected);
		}
	}

GWTでブラウザの履歴の処理に関連するのは主に以下の二つのクラスです。

  • History
  • Hyperlink

サンプルでは、タブがクリックされたらHistory#newItem()で履歴に新しいトークンを追加しています。このときURLにトークン(#以降の部分)が深されます。

History.newItem(TOKEN_TAB + event.getSelectedItem(), false);

1番目の引数がトークン(文字列)で、2番目の引数は履歴が追加されたイベントを発生させるかを指定します。

History.addValueChangeHandler(this);

のようにValueChangeHandlerインタフェースを実装したクラスをHistoryに登録することで、戻るボタンがクリックされた場合など履歴が変更された際のイベントをうけとります。

public void onValueChange(ValueChangeEvent<String> event) {
	if(event.getValue().startsWith(TOKEN_TAB)){
		int selected = Integer.valueOf(event.getValue().substring(event.getValue().indexOf(TOKEN_TAB) + 4));
		tab.selectTab(selected);
	}
}

onValueChangeにてトークンに応じて画面を遷移させるなどの処理を行ないます。実際のアプリケーションでは、この部分がコントローラの役割をになって画面を遷移させていくようなイメージになるのでしょうか??

アクセスされたURLにトークンが含まれている場合には、History#getToken()でトークンを取得することができるので、

if(!History.getToken().isEmpty()){
	History.fireCurrentHistoryState();
}

ブックマークから開いた場合にも画面を復元することができます。

また、Hyperlinkクラスをトークンを指定して生成することでリンクがクリックされたら自動的にトークンを追加することができます。

new Hyperlink("リンク文字列", "トークン");

なので、トークンを追加したい画面への遷移はHyperlinkを介するのがよいかもしれません。

GWT2.1からはplaceというパッケージが追加されるようです、もう少し抽象化したアプリケーションの状態やライフサイクルを管理するAPIが存在するようです。これもGDDでも少し聞きましたが、詳細はよく知りません。。。