ひだまりソケットは壊れない

ソフトウェア開発に関する話を書きます。 最近は主に Android アプリ、Windows アプリ (UWP アプリ)、Java 関係です。

まじめなことを書くつもりでやっています。 適当なことは 「一角獣は夜に啼く」 に書いています。

Windows ストアアプリ (Metro スタイルアプリ) における単一ページナビゲーションについて (JavaScript)

Windows 8 に新しく導入されるアプリは、もともと Metro スタイルアプリと呼ばれていましたが、現在は Windows ストアアプリと呼ばれるようになっています。

Windows 8 の Metro スタイルアプリの開発には、HTML + CSS + JavaScript を用いることができます。 Web ページと同じように複数ページ間のリンクを用いたナビゲーションもサポートされていますが、パフォーマンスの問題や状態管理の複雑さが増すこと *1 などから、単一ページでのナビゲーションが推奨されています。

上のページにおいて、JavaScript で Metro スタイルアプリを開発する際の単一ページナビゲーションの実現方法が説明されているのですが、WinJS.Navigation やら PageControl やら色々と出てきて初見ではよくわからなかったので、自分がわかりにくかったところを適当にまとめておきます。

全体像

上記ページには、いくつかの機能、オブジェクトが登場します。

  • ページコントロール (WinJS.UI.Pages.PageControl オブジェクト)
  • WinJS.Navigation
  • PageControlNavigator (navigator.js)

まず、これらの機能がどのような役割で、互いにどう関係しているのかが最初はよくわからなかったのですが、端的に述べると次のようになるようです。

  • ページコントロールは 「HTML + CSS + JavaScript で構成されるページ」 を表すためのオブジェクト
  • WinJS.Navigation は、ナビゲーションの履歴管理をするための navigation stack を含め、ナビゲーションを扱うための基本的な機能を提供するもの (これ自体がページの読み込みなどをするわけではなく、あくまでページ遷移などの情報を扱うもの)
  • PageControlNavigator は、上の 2 つの機能を使って、単一ページ上読み込まれたページコントロールの切り替えや、「戻る」 ボタンの機能提供をするもの

これ以降、個別の項目について述べていきます。

コントロールとは

そもそも 「コントロール」 というのが何なのか最初はよくわからなかったのですが、簡単に言ってしまえば次のようになるらしいです。

インタラクティビティを提供することが主な目的である要素とオブジェクトをコントロールと呼びます。

クイック スタート: HTML コントロールの追加とイベントの処理 (JavaScript と HTML を使った Metro スタイル アプリ)

要は、ユーザーからの入力を受け取ったり、ユーザーに何らかの状態を表示するための要素やオブジェクトのことのようです。 例えば HTML の button 要素はコントロールの一種です。

コントロールには HTML コントロールWinJS コントロール の 2 種類あります。 前者は通常の web 上で用いる HTML 要素と同じです。 後者は、Windows のライブラリとして独自に定義されているもので、使用するためには適切な JavaScript ファイルと CSS ファイルを使用するページ上で読み込む必要があります。 そして、多くの場合は以下のように div 要素にdata-win-control 属性を付けることで、要素に WinJS コントロールを結び付けます。

<div data-win-control="WinJS.UI.AppBar"></div>

上の例は、AppBar コントロール を使用する例です。 さらに必要なこととして、このようなコントロールを有効にするために、WinJS.UI.processAll メソッド の呼び出しを、ページのアクティベート時に行う必要があります。

詳細はドキュメントをご覧ください。

PageControl について

PageControl は、WinJS コントロールの一種です。 PageControl オブジェクトは、HTML、CSSJavaScript からなるページを表すためのオブジェクト です。 あるページの中に PageControl オブジェクトを埋め込むことができます。 この埋め込む PageControl オブジェクトを切り替えることで、単一ページ上であたかも複数のページに遷移しているような挙動をさせることができます。 *2 ページ中に PageControl オブジェクトを埋め込む方法はいくつかあります。

PageControl を使うためには、まず PageControl のコンストラクタを取得する必要があります *3。 PageControl のコンストラクタURI ごとに存在します。 例えばプロジェクト内に /sample_page_control.html という URI で表される HTML ファイルがあるとすると、この HTML とそこから読み込まれる JS と CSS で構成されるページを表す PageControl コンストラクタを得るには次のようにします。 (ちなみに PageControl として扱うための HTML + CSS + JavaScript の雛形は Visual Studio 上で簡単に作ることができます。)

var SamplePageControl = WinJS.UI.Pages.get( "/sample_page_control.html" );

さらに、この PageControl コンストラクタを使って PageControl オブジェクトを生成することで、アプリの画面として表示しているページ上にこの PageControl が表す HTML を埋め込むことができます。

<!-- PageControl オブジェクトを結びつける先の div 要素を以下のように用意しておく -->
<div id="sample-page-control"></div>
// PageControl オブジェクトが表す HTML を埋め込む要素を取得
var elem = document.getElementById( "sample-page-control" );
// 埋め込む先の要素を指定して PageControl のインスタンス
new SamplePageControl( elem );

これで、指定の要素の中に /sample_page_control.html の内容が表示されるはずです。

PageControl のコンストラクタを取得するための方法は他にもあり、別の方法として WinJS.UI.Pages.define メソッドを使用する方法があります。 このメソッドを使用すると、PageControl オブジェクトが画面上に埋め込まれた際や取り除かれた際の挙動を定義することができます。

指定の URI に対応する PageControl が既に生成されていた場合でも、WinJS.UI.Pages.define メソッド の呼び出しによって PageControl オブジェクトが画面上に埋め込まれた際や取り除かれた際の挙動を追加することができますので、PageControl として扱う HTML ファイルから読み込まれる JavaScript ファイルの中で WinJS.UI.Pages.define メソッドを使用して挙動を定義し、実際の PageControl のコンストラクタの取得は WinJS.UI.Pages.get メソッド を使うのが一般的なのかなぁ、と思ったりします。 ページ中に PageControl オブジェクトを埋め込む方法は他にもいくつかありますので、詳細はドキュメントを参照してください。

WinJS.Navigation について

WinJS.Navigation は、ナビゲーション、すなわちページの遷移を管理するための機能を提供します。 単一ページナビゲーションにおいては、ページ内に埋め込んでいる PageControl を別のものに変える際に WinJS.Navigation を使うことでページ遷移の管理を簡単に行うことができます。

WinJS.Navigation の API や使い方は以下のドキュメントとサンプルコードを見るとわかると思います。

遷移に際しての基本的な流れは以下のようになります。

  1. あらかじめ WinJS.Navigation.addEventListener メソッドを呼び出して、navigation 時に発生するイベントのリスナを登録しておく
    • 実際の遷移の処理はそのリスナから実行する
  2. WinJS.Navigation.navigate メソッドを呼び出し、遷移したいことを通知する
  3. WinJS.Navigation 上で beforenavigating イベントが発生する
  4. WinJS.Navigation 上で navigating イベントが発生する
  5. WinJS.Navigation 上で navigated イベントが発生する
    • 通常はイベントのリスナにおいて、実際のページ遷移の処理を行う模様 (サンプルコードなどを見た感じ)

ページ遷移をしたいことを WinJS.Navigation に伝えて、さらに WinJS.Navigation から実際のページ遷移を行うコードに処理が移る、という流れです。 これだけ見ると WinJS.Navigation を介する意味があまりないように見えますが、WinJS.Navigation は履歴管理を行ってくれるので、前のページに戻るとか、(前のページに戻った後に) 次のページに進む、といった処理が実装しやすくなります。

navigator.js について

単一ページナビゲーションのための以下の 2 つの機能は、上で説明した PageControl と WinJS.Navigation を使用することで実現できることがわかりました。

  • 単一ページ内に別のページの内容を埋め込み、埋め込みページを変えることで遷移を実現する
  • 遷移の履歴管理も行う

しかし、実際に実現しようとすると、navigated イベントを扱うコードを書いて、実際にページの切り替えを行うコードを書いて、といった作業が必要になります。 navigator.js は、そういった処理を行うコードが定義されている JS ファイルで、これを使うことで簡単に単一ページナビゲーションを実現できます。 navigator.js は、WinJS のライブラリに含まれているわけではないようですが、Visual Studio のプロジェクトテンプレートの一部 (分割アプリケーションテンプレートやナビゲーションテンプレートなど) にもともと含まれており、それらのテンプレートを使ってプロジェクトを新たに作成した場合は、そのまま使用することができます。

navigator.js の中では、Application.PageControlNavigator というコンストラクタが定義されています。 これは独自に定義されたコントロールのようで、他の WinJS コントロールと同じように使うことができるようです *4

navigator.js は単一ページナビゲーションを実現するコードの参考になるので、自前で書きたいという人も一度目を通してみるとよいかと思います。

まとめ

*1:全てのページでアプリケーションの停止や再開をサポートしなければならない

*2:iframe 要素や object 要素による web ページ中への別のページの埋め込みと似たようなものだと思えば良いかと思います

*3:WinJS.UI.Pages.render メソッドを使えば明示的にコンストラクタを取得しなくても良さそうです。

*4:独自コントロールの定義方法についてのドキュメント は、ページ自体はあるもののまだ内容がなかったので、よくわかりません。