イベントとコントローラ:インタラクティビティの追加

ユーザーが作成するほとんどのアプリケーションはインタラクティブ(対話式・双方向)です。 インタラクティブとは、ユーザーインターフェイス内でユーザーが行ったアクションに対してレスポンスすることを意味します。このインタラクティビティ(双方向性)のいくつかの例は、ユーザがボタンをクリックし、メニューから項目を選択する、またはフォームフィールドにテキストを入力するといったことです。これらすべてのケースでは、アプリケーションは次の2つを行うことが必須となります。

  1. ユーザーがビューに対して特定のアクションを行ったことを検出する
  2. それが発生したときに、あるコードを実行する

すべてのロジックがコントローラかヘルパークラスにある、ということが MVC アーキテクチャの基本原則であることを忘れないでください。ビューコンポーネントは GUI を描画だけです。ストア、モデル、プロキシはデータをインポートして保存するだけです。

Ext JSとSencha Touchアプリケーションでは、このタイプのインタラクティビティを処理する2つの方法があります。

  • イベントバインディング
  • コントローラアクション

両方のアプローチは明確な利点を持っているので、それぞれをどのような場面で使用するべきかを知っておくことが大切です。このガイドでは、Architect の中での各方法の使い方、および、どちらの方法を使うかの選択の仕方について説明します。

このガイドで構築された完全なサンプルプロジェクトは、ExtSimpleExamples の Git リポジトリで参照することができます。

以下の問いに関して考えてみましょう。たとえ両方の方法で多少類似したコードを生成できるとしても(下で示すように)、どうしてインタラクティビティを提供するために一方の方法のみを使用するのでしょうか。

イベントバインディング VS コントローラアクション

Architect では、アプリケーションの構築に MVC パラダイムを全面的に取り入れており、エレガントに分離(デカップル)されたアーキテクチャの構築を容易にし、その採用を促進します。したがって、ほとんどの場合には、基本的なイベントバインドよりも、コントローラアクションを使ってビューコンポーネントイベントをバインドすることをおすすめします。

主な違いは次の通りです。

  • Controller actions:Architectを利用してアプリケーションを構築する際のほとんどの場合に当てはまりますが、プロジェクトの最上位のクラスではない他のコンポーネントとのインタラクション動作の実装は、一般にコントローラに適しています。

  • Event handlers:“me” (this) のすべての子であるコンポーネントと対話動作を実装する場合、それは一般的にイベントハンドラに属します。基本的なイベントバインディングの使用が唯一許されるのは、イベントとそのハンドラのコードが1つのビューの中で完全に自己完結し、アプリケーションの他のビュー、モデル、またはコントローラに頼らないか影響を及ぼさない時です。言い換えると、基本的なイベントバインディングはビューによって引き起こされるイベントが同じビューに影響を及ぼすときのみに使用するべきです。次に示す単純なイベントバインディングは、このことを示す良い例です。ボタンが影響するのはその親パネルのサイズのみであり、アプリケーションの他の部分には影響しません。

イベントバインディングの使用

イベントバインディングは*イベントリスナ*とも呼ばれ、アプリケーションの特定のビュー内のコンポーネントに対して、ユーザーが行った操作によって発火される特定のイベントを「リッスン」(監視)し、そのビューのメソッド関数を呼び出します。発火やユーザーのアクションのイベントの種類によって、メソッドの関数には、特定の各種パラメータの組み合わせが渡されます。

非常に簡単な例を見てみましょう。ボタンを切り替えたとき、パネルのサイズを変更してみましょう。

  1. Ext JS 4.1のプロジェクトを作成します。
  2. キャンバスにPanelを追加します。
  3. Panelの子としてButtonを追加し、そのButtonのenableToggle設定をオンにします。
  4. Config パネルの Event Bindings の横にある “+” アイコンをクリックして、ボタンにイベントバインディングを追加します。
  5. Event Bindingのname設定をtoggleにします。Architect は、このバインディングに自動的に “onButtonToggle” という関数名 (fn) をつけますが、これは好きな名前に変更できることに注意してください。

これで、ボタンが切り替わるとアプリが onButtonToggle メソッドを呼び出すというイベントバインディングを持つボタンができました。Ext.button.Button-event-toggleの呼び出し時には、対象のボタンコンポーネントへの参照、それが現在押されているかいないか、イベントバインディングに指定された任意の追加オプション、の 3 つのパラメータが渡されます。

今度は、パネルのサイズを変更した場合に必要なアクションを実行するためのコードを追加する必要があります。インスペクタからイベントバインディングを探し(現在のラベルはtoggle *onButtonToggle*)、コードエディタで開くためにダブルクリックします。Architectは、エディタの上部にハンドラのパラメータを表示します。次のコードを挿入します。

if (pressed) {
    this.setSize(100, 200);
} else {
    this.setSize(400, 250);
}

プロジェクトを保存して、プレビューし、ボタンをクリックします。 パネルのサイズが変わるのが確認できるはずです。

Architectがこれを実行するために生成したコードを見てみましょう。

Ext.define('MyApp.view.MyPanel', {
    extend: 'Ext.panel.Panel',

    height: 250,
    width: 400,
    title: 'My Panel',

    initComponent: function() {
        var me = this;

        Ext.applyIf(me, {
            items: [
                {
                    xtype: 'button',
                    enableToggle: true,
                    text: 'MyButton',
                    listeners: {
                        toggle: {
                            fn: me.onButtonToggle,
                            scope: me
                        }
                    }
                }
            ]
        });

        me.callParent(arguments);
    },

    onButtonToggle: function(button, pressed, options) {
        if (pressed) {
            this.setSize(100, 200);
        } else {
            this.setSize(400, 250);
        }
    }
});

ハンドラのコードが含まれているonButtonToggleメソッドを呼び出すために、ボタンコンポーネントのtoggleイベントをリスナに追加していることがわかります。

また、このリスナは、最上位の Panel インスタンスを参照する変数 me のスコープ内で実行されるように設定されていることにも注意してください。Architect では常にイベントバインディングを設定し、それらを定義している最上位のビューコンポーネントのスコープ内で実行します。これにより、ハンドラコードの内の this キーワードからは、ハンドラメソッドを定義しているオブジェクト(イベントバインディングを追加した対象では*ない*)が常に参照されます。これは、サンプルのハンドラコードでthis.setSize()を呼び出して、ボタンではなくパネルのサイズが変わる理由でもあります。

イベントバインディングは、データストア、またはExt.util.Observableのように、ビューコンポーネントではないものに追加することができます。

注意 カスタムのリスナを Architect にハードコーディングすることはできません。

コントローラの使用

SenchaのMVCアーキテクチャの重要な部分はコントローラが、アプリケーションの様々なモデルとビューが互いについて特に知ることなく一緒に動作させることができる接着剤のような役割を提供していることです。アプリケーションの一部の厳密なデカップリングを維持し、完全に自己完結型の個々のビューを保持するのに役立ちます。これは、より堅牢でメンテナンスしやすいモジュール構造のアプリケーションにつながります。

コントローラで使用できるコンフィグオプションの詳細については、コントローラを参照してください。

コントローラアクションは、イベントバインディングと同様、アプリケーションが特定のビューコンポーネント上の指定された名前のイベントをリッスンして、ハンドラコードを実行することを可能にします。コントローラアクションは、特定のターゲットコンポーネントの一部として定義される代わりに、コントローラの一部として定義されます。結果として、どのコンポーネント(またはコンポーネントたち)がリッスンしなければならないか、コントローラアクションに指示する必要があります。これは、アクションのcontrolQueryに、適切なターゲットコンポーネント(複数可)を選択するためにExt.ComponentQueryセレクタを指定することによって行われます。

コントローラアクションを使用して、イベントバインディングの例を再現してみましょう。

  1. キャンバスにPanelを追加し、パネルにButtonを追加してenableToggle設定をオンにします。
  2. 後で使うので、ButtonのitemId設定をsizeToggleに変更します。
  3. 追加ボタン (“+”) を使用してアプリケーションにコントローラを追加します。
  4. Configパネルを介して、そのコントローラの子としてController Actionを追加します。プロンプトが表示されたら、ターゲットタイプ(アクションの targetType コンフィグ)として Ext.button.Button を、イベント(アクションの name コンフィグ)として toggle を選択します。このtargetTypenameの組み合わせは、Architectにイベントハンドラの正しいパラメータを調べさせます。
  5. 次に、コントローラにターゲットとなるボタンを教える方法として、コントローラアクションのcontrolQuery設定をセットします。多くの有効なクエリがありますが、この例では先ほど設定したButtonのitemIdに基づいたクエリを使用します。controlQuerybutton#sizeToggleを設定します。この “#” は id/itemId クエリを意味します。

最後に、イベントハンドラのコードを追加します。コントローラアクション(インスペクタ内でtoggle onButtonToggleという名前)をダブルクリックしてコードエディタを開き、次のコードを追加します。

var panel = button.up('panel');
if (pressed) {
    panel.setSize(100, 200);
} else {
    panel.setSize(400, 250);
}

このコードは、イベントバインディングのバージョンと若干異なります。コントローラハンドラは、パネルでなくコントローラのスコープ内で実行されるので、“this” キーワードを使用することはできず、パネルへの参照を明示的に取得する必要があります。

プロジェクトを保存とプレビューし、ボタンをもう一度クリックすると、パネルのサイズを切り替えられることがわかります。

ここでArchitectがコントローラ用に生成したコードは次の通りです。

Ext.define('MyApp.controller.MyController', {
    extend: 'Ext.app.Controller',

    onButtonToggle: function(button, pressed, options) {
        var panel = button.up('panel');
        if (pressed) {
            panel.setSize(100, 200);
        } else {
            panel.setSize(400, 250);
        }
    },

    init: function(application) {
        this.control({
            "button#sizeToggle": {
                toggle: this.onButtonToggle
            }
        });
    }
});

これはイベントバインディングで生成されたコードと似ているように見えますが、主な違いはキーとしてcontrolQueryをthis.control()で呼び出してリスナを設定していることです。

イベントバインディングからコントローラアクションへの変換

単純なイベントバインディングを使用していて、コントローラアクションにする必要があると判断した場合、Architectはそれを行うための簡単な方法を提供します。インスペクタでイベントバインディングを右クリックし、“Convert to Action >”にカーソルを合わせ、既存のコントローラか、Architectに新しいコントローラアクションの設置を伝えるために新しいコントローラ(“New Controller”)を選択します。Architect は、コントローラアクションの name 設定やコードを、元のイベントバインディングの値から取り込みます。controlQuery設定は手入力する必要があります。

Last updated