Architecting Your App in Ext JS 4, Part 3

SenchaのAPIドキュメンテーションにある Architecting Your App in Ext JS 4, Part 3 の翻訳をしました。これでMVC三部作の翻訳完了です。

とか思ったら、最後に “We will continue…” とある。まだ続くのかもしれません。

Architecting Your App in Ext JS 4, Part 3

Part 1 Part 2 の中で、PandoraスタイルアプリケーションをExt JS 4の新しい機能を用いて構築する方法を調べました。 複数のビュー、ストア、モデルのある複雑なUIにModel-View-Controllerアーキテクチャを適用することから始めました。 コントローラーからビューをコントロールする、 コントローラーがリスニングできるアプリケーションワイドなイベントを発火するのような、 アプリケーションを構築する基本的なテクニックを見てきました。 このパートでは、アプリケーションのMVCアーキテクチャの内部のコントローラーロジックの実装を続けます。

参照を取得する

アプリケーションの実装を再開する前に、Ext JS 4のMVCパッケージで利用可能な追加機能のいくつかを見てみましょう。 前のアーティクルにおいて、 Ext.application のコンフィグにsotresとmodels配列を追加することで、 アプリケーションにストアモデルを自動的にロードする方法を見てきました。 この方法でそれぞれのストアの生成されたインスタンスには、 その名前と同じstoreIdが与えられることについても説明しました。

app/Application.js

Ext.application({
    ...
    models: ['Station', 'Song'],
    stores: ['Stations', 'RecentSongs', 'SearchResults']
    ...
});

storesやmodels配列に追加すると、これらのクラスをインスタンス化するとともに、 自動的にゲッターも生成されます。 controllersやviewsにも当てはまります。 sotres, models, controllers, views の各コンフィグはコントローラークラスにもあって、 アプリケーションのインスタンスと同じ方法で正しく動作します。 StationsストアをStationコントローラーの中から参照するためには、 コントローラーのsotres配列にストアを追加するだけでよいということです。

app/controller/Station.js

...
stores: ['Stations'],
...

こうすると、自動的に生成されたゲッターメソッド getStationsStore を使って、コントローラーの中であればどこからでもStationsストアへの参照を取得できます。 名前の規則は次のように単純でわかりやすくなっています。

views: ['StationsList'] // creates getter named 'getStationsListView' -> returns reference to StationsList class
models: ['Station']     // creates getter named 'getStationModel'     -> returns reference to Station model class
controllers: ['Song']   // creates getter named 'getSongController'   -> returns the Song controller instance
stores: ['Stations']    // creates getter named 'getStationsStore'    -> returns the Stations store instance

重要で注意すべき、viewsとmodelsのゲッターはクラスへのリファレンスを返し、 storesとcontrollersのゲッターは実際のインスタンスを返すことです。

ビューインスタンスの参照

前のセクションで、stores, models, controllers, views コンフィグで 簡単にそれらの参照を取得できるように、 どのように自動的にゲッターが生成されるのか解説しました。 getStationsListView ゲッターメソッドはビュークラスへの参照を返します。 このアプリケーションの処理では、StationsList内の最初のアイテムを選択したいと考えています。 この場合は、ビュークラスへの参照は必要なくて、 ビューポート内にある実際のStationListインスタンスへの参照が必要になります。

Ext JS 3では、存在するコンポーネントインスタンスへの参照を取得する非常に一般的なアプローチは Ext.getCmp メソッドでした。 このメソッドは引き続き動作しますが、Ext JS 4においては推奨される方法ではありません。 Ext.getCmp を使ってアプリケーションの中で参照するためには、全てのコンポーネントにユニークなIDを与えなければなりません。 新しいMVCパッケージでは、 Ext JS 4の新しい機能である ComponentQuery を活用する事によって コントローラー内にビューインスタンスへのリファレンスを置くことができます。

app/controller/Station.js

...
refs: [{
    // A component query
    selector: 'viewport > #west-region > stationslist',
    ref: 'stationsList'
}]
...

refsコンフィグで、ビューのインスタンスへの参照をセットアップできます。 これによりコントローラーのアクションの中からページ上のコンポーネントを参照し操作できます。 参照したいコンポーネントを指定するにはselectorプロパティの中でComponentQueryを使うことができます。 オブジェクトの中に他に必要な情報は ref プロパティです。 refs配列内にある各アイテムに対して自動的に生成されるゲッターの名前の一部として使われます。 例えば、 ref: 'stationsList' (注: 大文字のLに注目) と定義すると、 getStationsList というゲッターがコントローラー上に生成されます。 一方、コントローラー内に参照を設定しない場合には、 Ext.getCmp をコントローラーアクション内で使い続けることになります。 しかし、この方法はオススメしません。 なぜならプロジェクト内でユニークなコンポーネントIDの管理をしなければならなくなってしまい、 プロジェクトが大きくなると問題を起こすことがあるからです。

忘れてはいけない大事なことは、これらのゲッターはページ上にビューが実際に存在するかどうかとは関係なく生成されると言うことです。 ゲッターをコールしたときにselectorがページ上のコンポーネントの一致すると、 結果はキャッシュされるのでその後のゲッターの呼び出しはより速くなります。 しかし、selectorがページ上のビューと一致しなかった場合は、ゲッターはnullを返します。 これが意味することは、ビュー上に依存するロジックがありビューがページ上にまだ存在しない可能性がある場合には、 ゲッターが結果を返した場合だけ実行されるようにするチェックロジックを追加する必要があると言うことです。 また複数のコンポーネントがselectorに一致した場合には、最初の一つだけが返されます。 従って、取得したい一つのビューだけを特定するselectorを作るのがよい方法です。 最後に、参照しているコンポーネントが廃棄(destroy)された場合、 ページ上に一致する他のコンポーネントがない限り、ゲッターは再びnullを返すようになります。

アプリケーション起動時にコントローラーロジックをカスケードする

アプリケーションの開始時、ユーザーの既存のステーションをロードしたいと思います。 アプリケーションの onReady メソッドの中にこのロジックを置くことができますが、 MVCアーキテクチャには 全てのコントローラー、モデル、ストアがインスタンス化されて初期ビューがレンダリングされた直後に 各コントローラーで発火する onLaunch メソッドがあります。 これによりグローバルアプリケーションロジックとコントローラー特有のロジックを綺麗に分離することができます。

Step 1

app/controller/Station.js

...
onLaunch: function() {
    // Use the automatically generated getter to get the store
    var stationsStore = this.getStationsStore();
    stationsStore.load({
        callback: this.onStationsLoad,
        scope: this
    });
}
...

StationコントローラーのonLaunchメソッドはStrationストアのloadメソッドを呼び出す最適な場所に思えます。 ご覧のように、ストアがロードされた直後にコールバックが実行されるようにも設定しています。

Step 2

app/controller/Station.js

...
onStationsLoad: function() {
    var stationsList = this.getStationsList();
    stationsList.getSelectionModel().select(0);
}
...

このコールバックの中で自動的に生成されたゲッターを使ってStationsListインスタンスを取得し、 最初のアイテムを選択しています。 これによりStationsListの selectionchange イベントが発火します。

Step 3

app/controller/Station.js

...
init: function() {
    this.control({
        'stationslist': {
            selectionchange: this.onStationSelect
        },
        ...
    });
},

onStationSelect: function(selModel, selection) {
    this.application.fireEvent('stationstart', selection[0]);
},
...

アプリケーションイベントはアプリケーションの中にイベントをリッスンするようなコントローラーが多く張るような場合にとても便利です。 これらのコントローラーで同じビューイベントをリッスンする代わりに、 一つのコントローラーがビューイベントをリッスンして他のコントローラーがリッスンできるように アプリケーションワイドなイベントを発火します。 またコントローラーが他のコントローラーとお互いについて知ったり依存したりせずに連携できるようになります。 onStationSelect アクションの中で、 stationstart というアプリケーションイベントを発火しています。

Step 4

app/controller/Song.js

...
refs: [{
    ref: 'songInfo',
    selector: 'songinfo'
}, {
    ref: 'recentlyPlayedScroller',
    selector: 'recentlyplayedscroller'
}],

stores: ['RecentSongs'],

init: function() {
    ...
    // We listen for the application-wide stationstart event
    this.application.on({
        stationstart: this.onStationStart,
        scope: this
    });
},

onStationStart: function(station) {
    var store = this.getRecentSongsStore();

    store.load({
        callback: this.onRecentSongsLoad,
        params: {
            station: station.get('id')
        },
        scope: this
    });
}
...

Songコントローラーのinitメソッドで、 stationstart アプリケーションイベントのリスナーを設定しています。 これが起こったときには、 RecentSongsストアにこのステーションの曲をロードする必要があります。 それを onStationStart メソッドで処理します。 RecentSongsストアへの参照を取得してそのloadメソッドをコールし、 ロードが終了した直後に発火するコントローラーアクションを定義しています。

Step 5

app/controller/Song.js

...
onRecentSongsLoad: function(songs, request) {
    var store = this.getRecentSongsStore(),
        selModel = this.getRecentlyPlayedScroller().getSelectionModel();

    selModel.select(store.last());
}
...

RecentSongsストアのなかにステーションの曲がロードされたときに、 RecentlyPlayedScrollerの最後の曲を選択します。 そうするために RecentlyPlayedScroller dataview 上のセレクションモデルを取得し、 そのselectメソッドにRecentSongsストア内の最後のレコードを渡してコールしています。

Step 6

app/controller/Song.js

...
init: function() {
    this.control({
        'recentlyplayedscroller': {
            selectionchange: this.onSongSelect
        }
    });
    ...
},

onSongSelect: function(selModel, selection) {
    this.getSongInfo().update(selection[0]);
}
...

スクローラーの中の最後の曲を選択したとき、 selectionchange コントロールメソッド (このイベントのリスナーはすでにセットアップしていて onSongSelectメソッドの中にあります) の中で、 SongInfoビューの中のデータがアップデートすることによって アプリケーションフローが完了します。

新しいステーションを始める

これで、アプリケーションフローを追加実装するのが簡単になりました。 新しいステーションを作成して選択するロジックを追加するのは次のようになります。

app/controller/Station.js

...
refs: [{
    ref: 'stationsList',
    selector: 'stationslist'
}],

init: function() {
    // Listen for the select event on the NewStation combobox
    this.control({
        ...
        'newstation': {
            select: this.onNewStationSelect
        }
    });
},

onNewStationSelect: function(field, selection) {
    var selected = selection[0],
        store = this.getStationsStore(),
        list = this.getStationsList();

    if (selected && !store.getById(selected.get('id'))) {
        // If the newly selected station does not exist in our station store we add it
        store.add(selected);
    }

    // We select the station in the Station list
    list.getSelectionModel().select(selected);
}
...

概要

これまで先進的なコントローラ技術を使用してビューからロジックを分離することによって、 アプリケーションのアーキテクチャがわかりやすく保守しやすくなることを説明しました。 この段階でアプリケーションはすでにかなり機能しています。 新しいステーションを検索し追加でき、ステーションを選択するとそれを開始できます。 ステーションの曲はロードされ、曲とアーティストの情報が表示されます。

このシリーズの次のパートではスタイリングやカスタムコンポーネントの作成に焦点を当てて、 アプリケーションの改良を続けます。

プロジェクトファイルをダウンロード

1 thought on “Architecting Your App in Ext JS 4, Part 3

  1. Anisha

    سÙ„اÙÂ…ÙÂ…Ù† Û±Û¶ فرÙˆردÛŒن Ù…صاØ­بÙ‡ رؘ§ÃÃÂÂÙÂ….Ú†¯™Â†Ø¯ رÙˆز Ù†ÛŒاز است تا بتونÙÂ… بعد از Ù…صاØ­بÙ‡ تشکÛŒل پروندÙ‡ فدراÙ„ بدÙ…؟فÛÂŒ رÙˆ باÛŒد بÙ‡ دÙ„ار کاÙ†ادا با خودÙÂ… ببرÙÂ…ØŸ

コメントは停止中です。