Sencha Touch 2を使用したアプリケーション入門

Sencha Touch 2.0 のRCが公開されています。 そのドキュメントのガイドにある Intro to Applications with Sencha Touch 2 を翻訳しました。

Sencha Touch 2を使用したアプリケーション入門

Sencha Touch 2は、複数のプラットフォームで動作するアプリケーションを構築することに最適化されています。 できる限り簡単にアプリケーションを構築するために、 シンプルですがパワフルなMVC(モデル、ビュー、コントローラー)パターンを実現するアプリケーションアーキテクチャーを提供します。 このアプローチはコードをクリーンでテスト可能で保守しやすく保ち、アプリケーションを書く上でのいくつかの恩恵を提供します。

  • 履歴のサポート: アプリケーション内で戻るボタンをサポートし、アプリケーションのどのバートにもリンクできるようにします。
  • ディープ リンク: ウェブページへのリンクのようにアプリケーションのスクリーンを開くディープリンクを提供します。
  • デバイス プロファイル: 携帯電話、タブレット、他のデバイスによって簡単にアプリケーションのUIをカスタマイズでき、 共通のコードを共有できます。

アプリケーションの構造

アプリケーションは Model, View, Controller, Store, Profile さらにアプリケーションアイコンや起動画面の画像などを指定する いくつかのメタデータの集合体です。

architecture-overview

  • Model: アプリケーションのオブジェクトのタイプを表現します。例えばeコマースのアプリであればUsers, Products, Orders などのモデルを持つことになります。
  • View: ユーザーにデータを表示しSencha Touchのビルトインコンポーネントを活用します。
  • Controller: アプリケーションとのやりとりをハンドリングし、ユーザのタップやスワイプをリスニングし、それに応じたアクションを起こします。
  • Store: アプリにデータをロードしてListやDataViewなどのコンポーネントに供給します。
  • Profile: 多くのコードを共有したままアプリケーションのUIをタブレットや携帯電話ごとに簡単にカスタマイズできるようにします。

アプリケーションはまずSencha Touchアプリケーションを定義することから始めるのが普通です。次のようになります。

[js] Ext.application({ name: ‘MyApp’, models: [‘User’, ‘Product’, ‘nested.Order’], views: [‘OrderList’, ‘OrderDetail’, ‘Main’], controllers: [‘Orders’], launch: function() { Ext.create(‘MyApp.view.Main’); } }); [/js]

nameコンフィグはアプリケーション全体で使う1つのグローバルネームスペースを生成するために使います。 すべてのモデル、ビュー、コントローラーなどのクラスはこのネームスペースに含まれます。 MyAppというアプリでは、それを構成するクラスは MyApp.model.User, MyApp.controller.Users, MyApp.view.Main などのようなパターンになります。 これによりアプリ全体が1つのグローバル変数の下に保持されますので、ページ上の他のコードと衝突を起こす可能性が最小になります。

アプリケーションに models, views, controllers コンフィグを定義すると、これらのクラスをアプリに自動的にロードします。 簡単なファイル構造規則 (モデルはapp/modelディレクトリに、コントローラーはapp/controllerディレクトリに、ビューはapp/viewディレクトリに) に従います。 例) app/model/User.js, app/controllers/Orders.js, app/view/Main.js

modelsに指定したうちの最後の1つが他と違ってますね。 これはフルクラス名でいうと (“MyApp.model.nested.Order”) になります。 標準の名付け規則に従わない場合には、これらのコンフィグではフルクラス名を指定することができます。 カスタムの依存関係を指定する方法については Ext.app.ApplicationのドキュメントにあるDependenciesのセクション を参照してください。

コントローラー

コントローラーはアプリケーションをまとめる接着剤です。

UIで発火されたイベントをリッスンしてそのアクションを実行します。 これによりコードが綺麗になり読みやすくなりコントロールロジックからビューを隔離することができます。

例えば、ログインフォームを使ってログインする必要があるとしましょう。 この場合のビューは入力フィールドや他のコントロールのあるフォームです。 コントローラーはフォームの送信ボタンのtapイベントをリッスンして、認証作業を実行します。 データや状態の操作があったときには、ビューではなくコントローラーがその変更を処理するべきです。

コントローラは小さなしかしパワフルな機能のセットを公開します、 そしてちょっとした単純な規則に従います。

アプリケーション内の各コントローラーはExt.app.Controllerのサブクラスです (既存のコントローラーのサブクラスでもいいのですが、それもExt.app.Controllerを継承していることに違いはありませんね)

コントローラーはMyApp.controller.*ネームスペースに存在します。 例えばSessionsコントローラーがあるとすると、その名前は MyApp.controller.Sessions となり、ファイル名は app/controller/Sessions.js. となります。

コントローラーはExt.app.Controllerのサブクラスですが、それぞれはExt.app.Applicationによってロードされるときに一度だけインスタンス化されます。

同時に1つしかインスタンスは存在せずコントローラーのインスタンスのセットはApplicationの内部で管理されます。 Applicationのcontrollersコンフィグを使うと、コントローラーをロードし自動的にインスタンス化します。

シンプルな例

上記で説明したSessionsコントローラーを定義します。 ここでは2つのコンフィグを指定しています。refs と control です。 refsはページ上のコンポーネントを見つけ出す簡単な方法です。 この場合ですとxtypeがformpanelである全てのコンポーネントにマッチし、 見つかった中の最初のものがloginFormプロパティに割り当てられます。

このプロパティは後でdoLogin関数内で使います。

次はcontrolコンフィグです。 これもrefsのようにComponentQueryセレクターを使ってxtypeがformpanelの中にあるbuttonを探します。 (この例では、ログインフォームにあるSubmitボタンが見つかります) このタイプのボタンがtapイベントを発火した場合は、doLogin関数がコールされます。

[js] Ext.define(‘MyApp.controller.Sessions’, { extend: ‘Ext.app.Controller’, config: { refs: { loginForm: ‘formpanel’ }, control: { ‘formpanel button’: { tap: ‘doLogin’ } } }, doLogin: function() { var form = this.getLoginForm(), values = form.getValues(); MyApp.authenticate(values); } }); [/js]

doLogin関数の本体は非常に簡単です。 ref に ’loginForm’ を定義したので、コントローラーは一致するformpanelを返すgetLoginForm関数を自動的に生成します。 このフォームへの参照から値(usernameとpassword)を引っ張り出して、authenticate関数に渡します。 これら – イベントの発火(大概はUIからの)をリッスンしてアクション(この場合は認証)をキックする – がほとんどのコントローラーがやることです。

コントローラとは何でどんな機能があるのかについて詳しくは コントローラーのガイド を参照してください。

ストア

ストアはSenca Touch の重要なパートでデータ連係ウィジェットのほとんどにデータを供給します。 簡単に言うと、ストアはModelインスタンスの配列以上のものではありません。 ListやDataViewのようなデータ連係コンポーネントはストアの中の各Modelインスタンスの1つのアイテムだけを描画します。 Modelインスタンスがストアに追加されたり削除されたりするとイベントが発火し、それをデータ連係コンポーネントがリッスンして自分自身を更新します。

ストアのガイド には ストアが何でありアプリのコンポーネントとどのようにフィットさせるのかについて より多くの情報があります。 そこには、Applicationインスタンスと統合する意識しておくべきいくつかの具体的なポイントがあります。

デバイス プロファイル

Sencha Touchは、能力やスクリーンサイズが異なる幅広い範囲のデバイスで使われます。 タブレットで動作するユーザーインターフェースは携帯電話ではうまく動かないかもしれませんし、その逆もまた然りですので、違ったデバイスタイプ用にカスタマイズされたビューを提供するのは当然です。

しかしながら、違ったUIを提供するだけのために何度もアプリケーションを書きたくはありません。 できるだけ多くのコードを共有したいと考えます。

デバイス プロファイルはアプリがサポートする違ったタイプのデバイスを定義することができるシンプルなクラスで、 各デバイスは違った方法で処理されます。 これらはオプトインなので、最初はプロファイルなしで開発してあとで追加することも、プロファイルを使わないこともできます。 各プロファイルには、 プロファイルがアクティブになるべき時にはtrueを返すisActive関数や、 プロファイルが決定されたときにロードする models views controllers コンフィグを定義します。

アプリがプロファイルをサポートするようにするには、 これらのプロファイルをApplicationのprofilesコンフィグに設定すると、 ApplicationはExt.app.Profileのサブクラスを生成します。

[js] Ext.application({ name: ‘MyApp’, profiles: [‘Phone’, ‘Tablet’], //as before }); [/js]

上記のように設定するとApplicationはapp/profile/Phone.js と app/profile/Tablet.jsをロードします。 アプリのタブレットバージョンでは追加機能が利用可能になる グループ管理の例を見てみましょう。 次がTabletプロファイルの定義例です。

[js] Ext.define(‘MyApp.profile.Tablet’, { extend: ‘Ext.app.Profile’, config: { controllers: [‘Groups’], views: [‘GroupAdmin’], models: [‘MyApp.model.Group’] }, isActive: function() { return Ext.os.is.Tablet; } }); [/js]

isActive関数はアプリケーションがSencha Touchがタブレットであると判断したもので動作している場合にtrueを返します。 これは少々主観的な判断になります。というのもデバイスのシェイプやサイズはほぼ連続的に分布していて、携帯電話とタブレットの間の明確な分岐点はないからです。 どのデバイスが携帯電話でどれがタブレットなのかという判断をする確実な方法がないので、 Sencha Touchの Ext.os.is.Tableは、iPadで実行しているときにはtrueそれ以外はfalseを返します。 より細かなコントロールをしたい場合には、isActive関数で好きなように実装を提供してtureまたはfalseを返すようにします。

isActive関数がtrueを返すプロファイルは1つだけにしなければなりません。 複数のプロファイルがtrueを返す場合は、最初の1つだけがカウントされ他は無視されます。 trueを返す最初の1つがApplicationのcurrentProfileにセットされ、それはあとから参照できます。

currentProfileに models,, views, controllers, sotres コンフィグが定義されていたら、 Applicationは、自身で定義された全てのmodels, *views, controllersとともにそれらを自動的にロードします。 しかし、プロファイルの中で名付けられた依存関係は、 完全修飾形式のクラス名が指定されない限りプロファイル名が前につけられたものになります。

  • views: [‘GroupAdmin’] では app/view/tablet/GroupAdmin.js がロードされます。
  • controllers: [‘Groups’] では app/controller/tablet/Groups.js がロードされます。
  • models: [‘MyApp.model.Group’] では app/model/Group.js がロードされます。

ほとんどの場合は、プロファイルは追加のコントローラーやビューを追加するだけで、モデルやストアは全てのタイプのアプリの間で共有されるのが普通です。

プロファイルについてのより詳しい説明は device profiles guide を参照してください。

起動プロセス

各Applicationではlaunchメソッドを定義できます。 これはアプリのクラスがロードされアプリを起動する準備ができたらコールされます。 This is usually the best place to put any application startup logic, typically creating the main view structure for your app. これは、アプリケーションのスタートアップロジック (よくあるのがアプリのメインビュー構造を生成するなど) を書く最良の場所です。

Applicationのlaunch関数の他に、スタートアップロジックを配置する場所があと2つあります。 まず各コントローラーにはinit関数を定義することができます。この関数はApplicationのlaunch関数の前に呼び出されます。 次にデバイスプロファイルを使っている場合には各プロファイルにlaunch関数を定義できます。 これはコントローラーのinit関数の後、Applicationのlaunch関数の前にコールされます。

注意すべきはアクティブなプロファイルのlaunch関数だけが呼び出されるということです。 例えば、PhoneとTabletのプロファイルが定義されていてタブレットで実行された場合には、 Tabletプロファイルのlaunch関数だけがコールされます。

  1. Controller#init 関数が呼ばれる
  2. Profile#launch 関数が呼ばれる
  3. Application#launch 関数が呼ばれる
  4. Controller#launch 関数が呼ばれる

プロファイルを使う場合にはプロファイルのlaunch関数の中に起動ロジックのほとんどを配置するのが普通です。 なぜなら各プロファイル毎に開始時に組み立てられる必要があるビューのセットが違うからです。

ルーティングと履歴のサポート

Sencha Touch 2 はルーティングと履歴を完全にサポートします。 Kitchen SinkなどのいくつかのSDKサンプルは、スクリーン間でのナビゲートを簡単にする戻るボタン(特にAndroidで便利)をサポートするために履歴サポートを使っています。

履歴サポートのドキュメントはbeta1以降に公開されます。(訳注: すでに公開済み) 2.0.0 PR4時点でのSencha Touch の履歴サポートを学ぶ最適な場所はKitchin Sinkサンプルです。 これは履歴サポートのために必要なルーティングと状態回復の沢山のドキュメントを備えています。

参考文献

Sencha Touch 2 でのアプリケーション アーキテクチャーを使うことに関するガイドがいくつかあります。