Ext JS 4.0/4.1 MVCアプリケーション構造の理解 – 株式会社ゼノフィ社内勉強会資料

本資料は、2011年12月18日に開催された株式会社ゼノフィ社内勉強会資料です。

株式会社ゼノフィ

アジェンダ

  • Class Styles
  • Ext JSにおけるアプリケーションパターン
  • Ext.app.Controller/Ext.Application
  • サンプルアプリケーションレイアウト
  • 各コントローラー作成
  • コントローラーからビューへのアクセス

Class Styles

  • Ext JS 3までの実装は、Ext.extendを利用してコンポーネントクラスを継承し、initComponentで必要な初期化を行いアプリケーションを実装。
  • コンポーネントを一つのクラスとして実装する手法は非常に有効であるが、見た目(View)と実装(Model/Controller)が混在するクラスが作成されてきた。

Ext JS 3 Style Class

Ext JS 4 Style Class

Ext JS 4のクラスシステムについては、Ext JS 4 実践開発ガイドに記載されているとおりで、クラスシステム自体をきっちり理解しておくこと。

Ext JSにおけるアプリケーションパターン

大きく分けて、2パターン、複合するものを加えると3パターン。

  • 1画面(スクリーン)に対して1つのHTML
  • 多画面(スクリーン)に対して1つのHTML
  • または、この複合

1画面(スクリーン)に対して1つのHTML

一覧編集」の画面があった場合、通常のHTMLベースのWEBアプリケーションと変わらず、index.htmlで一覧edit.htmlで編集、とした場合。

メリット

  • 1ページで生成されるDOMがある程度制限される。
  • ページごとにコンポーネントが分割されるため干渉する可能性が低くなる。

デメリット

  • 操作(スクリーン)を切り替える度に、HTMLおよびそれに付随するコンテンツの読み込みが発生する。

Sencha Touchでのモバイル開発において、スペックの低いデバイスに関しては、この方法以外選択肢はない。
次の多画面を1ページに入れ込むと速度的にも厳しい物になるので、モバイルの場合は1スクリーン1画面か、複合になる。

多画面(スクリーン)に対して1つのHTML

アプリケーション全ての画面を、1つのHTMLで表現する。基本はCardレイアウト。

メリット

  • 画面移動の度にHTMLリクエストを投げるわけではないので、切り替えがスムーズ。
  • 「戻るボタン」の制御もExt.util.Historyで制御できるため、別ページに遷移しているかのような動作が可能。

デメリット

  • 1ページに作成されるDOMの数が、前者より増大する。
  • DOMが増大するということは、遅くなる可能性が高まる。

全体の画面量に応じて使い分けるべきであるが、PC版に関しては、多画面に対して1つのHTMLを利用した方が切り替えの速度的によい。
しかし、アプリケーションの規模に応じ、一部はExt.util.HistoryとCardレイアウトを利用して移動するが、ある程度の単位でHTMLを切り替えるというのが、複合である。

Ext.app.Controller/Ext.Application

Ext JS 4からは、脱Ext.onReadyをする。Ext.onReadyが利用できないわけではないし、Ext.onReadyと平行利用しても問題はない。
Ext.onReadyはあくまで、イベントハンドラの登録であるため。ただし、body.onloadとイコールではない。なぜならば、コンテンツの読み込み完了時に呼び出されるのではなく、Ext.Loaderによる依存関係の解決も完了してから呼び出されるためである。

MVC形式でアプリケーションを記述し始める場合、Ext.Applicationを記述することから始める。
Ext.Applicationクラスは、Ext.app.Controllerを継承したクラスであり、シングルトンで定義されているため、ユーザーがインスタンス化することはない。

name/appFolder/controllersオプション

Ext.applicationにnameを指定すると、アプリケーションの名前が設定できる。appFolderは、この後利用するコントローラーやモデル、ストアなどを読み込みにいくパスを指定する、未指定の場合は、同一ディレクトリに指定のディレクトリが存在する前提で動作する。
controllersは、このアプリケーションで利用するコントローラー名を文字列で定義する。
これらを全て設定すると、Code1のようになる。

Code1 to GitHub

controllerを配置する

さて、アプリケーションを作成したら次にコントローラーを配置します。
とにもかくにも、コントローラーを設置して動かしてみましょう。
次のようなクラスを、app/controller/Main.jsとして作成します。

ここで重要なのは、extendでExt.app.Controllerクラスを必ず継承する必要があるということです。
また、自動的にinitメソッドが呼び出されます。このタイミングでwindow[‘アプリケーション名’]のオブジェクトが作成されています。
このオブジェクトをアプリケーションオブジェクトといいます。
アプリケーションクラスオブジェクトは、コントローラーのthisから、this.applicationでアクセスします。
この2つは似ていますが、前者はコントローラークラスの定義などが格納されるものであり、Ext.app.Applicationのオブジェクトとして扱う場合には、this.applicationで参照される方を利用します。

Code2 to GitHub

viewを配置する

さて、Code2まででアプリケーションクラスオブジェクトの作成と、Mainコントローラーの作成はできましたが、画面には何も表示されていません。
次に、この画面にあたるviewを作成していきます。わかりやすくViewportコンポーネントを表示してみましょう。

と、先に進む前に、このMVCアプリケーション形式ではViewportに限り2つの作成方法があります。
1つは、ユーザーが自分で作成する、2つ目はExt.Applicationクラスに任せるという方法です。
まずは、自分でViewportを作成する方法からやってみましょう。

src/app/view/Viewport.jsというファイル名で次のようなクラスを定義します。

Ext.container.Viewportを継承したクラスを作成します。
さらに、Viewportは作成しただけでは画面の周りにうっすらとborderが引かれるだけで、わかりにくいのでコンテナの中にパネルを作成します。

では、作成したXenophy.view.Viewportをインスタンス化して表示させましょう。
src/app.jslaunchに処理を記述します。

ファイルをHTMLで読み込んでいないのに表示されるのは、Ext.Loaderで読み込んでいるからです。
これで、Viewportが表示されるようになります。

Code3 to GitHub

では、もう一つの方法でExt.ApplicationにViewportを作ってもらうように、修正してみます。

launchの処理は空になりましたが、autoCreateViewportをtrueに設定しています。
実はこれだけです。
autoCreateViewportをtrueに設定することにより、Ext.Applicationが内部的にロード、生成を行ってくれます。
通常は、この方が簡潔でよいでしょう。注意点としては、ちゃんとview/Viewport.jsというファイル名になっていて、かつ、アプリケーション名が先頭つくクラス名(ここでは、Xenophy.view.Viewport)になっている必要があります。
この命名規則は、おきまりだと覚えておくとよいでしょう。

Code4 to GitHub

サンプルアプリケーションレイアウト

ここまでで、コントローラーを作成して、ビューを作成し、Viewportが表示できました。
MVC形式をとらなければ、Ext.onReadyにViewportを生成するだけの行程と全く同じ結果しか得られていません。
なのにMVC形式の場合、ディレクトリ作成したりコントローラー作成したり等手間が多かった気がします。
しかし、本領発揮はこれからです。

さて、とりあえずビューポートだけでは寂しいので、次のようなサンプルアプリケーションを作成してみましょう。

Viewportの中に、次のパネルを作成します。

  • Xenophy.view.Header
  • Xenophy.view.Navi
  • Xenophy.view.Center

Xenophy.view.Header

Xenophy.view.Navi

Xenophy.view.Center

Xenophy.view.Viewport

Header/Navi/Centerパネルには、aliasを指定してViewportではxtypeで指定できるようにします。
そのため、クラス定義にalias:’widget.XXXXX’でxtypeを定義しましょう。
widget.XXXXXって何ですか?xtypeとの関係は?って人は、実践開発ガイド読んでください。

それぞれのパネルも同様にExt.Loaderで読み込むことになります。
従って、Viewportでxtypeを利用するには既にそれぞれのパネルクラスが読み込み、定義が終わっている必要があります。
Viewportクラスにusesに配列で設定します。
これで、Viewportクラスが生成されるタイミングで、それぞれのパネルは読み込みが完了しています。

viewportはborderレイアウトでレイアウトします。

2つほどポイントを押させておきます。1つは、layoutに指定しているオブジェクトリテラルです。
これは、layout: ‘boreder’と書いてもborderレイアウトでレイアウトされます。
テクニックですが、viewportは、padding:5 くらいとってあげたほうが見やすくなります。
で、いままではそれぞれのパネルがpaddingをとっていたと思いますが、これからは、borderレイアウトのコンフィグオプションとして設定したほうが綺麗に書けます。

もう一つは、region,width,heightは、クラス定義ではなく配置時に設定すると言うことです。
これは、Ext JS 3から変わっていません。
時々splitをNaviクラスに設定して、動かないとか、そういう話を聞くことがありますが、region/splitなど、borderレイアウトが参照する設定は、配置するコンポーネント側で設定してください。

Code5 to GitHub

各コントローラー作成

さて、Mainコントローラーを作成しただけで、それ以外のコントローラーを作成していません。
もっといってしまえば、コントローラーは作成しただけで、何も利用していませんでした。
ここでは、各コントローラーを作成していきます。

コントローラーは、何らかの制御処理を行う部分です。制御するものが無いと、存在価値が0なので、Headerパネルにボタンを配置して、クリックを制御します。
さらに、HeaderをパネルではなくてContainerに変更します。
これは、大概のアプリケーションでヘッダーは必要であったり無かったりすると思いますが、必要な場合Panelである必要がほとんど無いからです。
PanelとContainerの場合、Containerの方が親クラスでPanelに比べて機能が少ない分、高速です。

ここで作成するアプリケーションは、次のようになります。

アイコンを表示するなどしますので、cssファイルを作成します。

resouces下にCSSとアイコンファイルを配置します。
詳しくは、githubのコードを確認してください。

ここでは、ボタンを制御するコントローラーとしてHeaderコントローラーを作成します。
src/app/controller/Header.jsになります。

src/app/controller/Header.js

initで初期化が行われるタイミングで、controlメソッドを利用してイベントハンドラを設定していきます。
この時、どのコンポーネントに対してのイベントハンドラを定義するかは、コンポーネントクエリーを利用します。
コンポーネントクエリーの使い方はドキュメントを参照してしてください。

上記の場合、自分で作成した「xenophy-header(xtype名)の中にある、button(xtype名)のaction属性が=以降の物」を指しています。

さて、これに対応するビューを作成しなくてはなりません。
先ほどCode5で作ったコードを修正します。

src/app/view/Header.js

ツールバー内のボタンに対して、action属性を指定しているのがわかります。
これで、ボタンを押すとアラートメッセージが表示されるようになります。

同様に、Navi/Centerのコントローラーも用意しましょう。
ここでは、特に処理が無いので、initが空のクラスになります。

コントローラー使用宣言

src/app.js

それぞれ、コントローラーを作成しましたが、app.jsにてMainコントローラー同様、利用することを宣言する必要があります。
controllersの配列に追加します。これをしないと、コントローラーとして動作してくれないし、読み込まれもしないので気をつけてください。

さて、ここまでで、コントローラを作成して、ビューを制御することがとりあえずできました。
Ext JS 3の時のように、initComponentで自分の中のコンポーネントオブジェクトに対してイベントハンドラを記載する方法と大きく異なっていますね。
これが「viewから処理が切り離された」ことになります。

では、Xenophy.view.Headerではなくて、controlメソッドの定義を、Mainコントローラーに書いたらどうなるでしょうか?
結果、もちろん動きます。
あえて、Xenophy.view.Header/Xenophy.controler/Headerと対になるように作成しましたが、その関係に縛りはありません。
Mainコントローラーの中だけで全てを処理してもかまわないわけです。
しかし、設計の問題もありますが、通常、1viewに対して、1controllerを作成するのが望ましいでしょう。
必須ではありませんが、要所要所X個のviewに対して、1controllerでもかまいません。

イベントハンドラ内のthis

補足ですが、先ほどのclickイベントハンドラ内のthisは何になるでしょうか?スコープは?
答えは、「定義されているコントローラークラスオブジェクト」になります。
initComponentで定義する感覚では、スコープを指定しない場合、該当のコンポーネントがthisになると思いますが、
コントローラーで定義した場合は、定義したコントローラーのオブジェクトがthisになります。
これは、非常に都合がよいのです。
コントローラーは、ここまで説明してしてきている機能以外にも大変便利な機能が存在します。
そこへアクセスするのに、thisでアクセスすることが簡単にできます。

Code6 to GitHub

コントローラーからビューへのアクセス

Code.6で作成してきたコードをみると、勘違いしやすい点として「コントローラーオブジェクトとビューオブジェクトはイコールではない」ということです。
当たり前と言えば当たり前なのですが、名前空間、配置的に同じ名前が使用され、その名前空間がちょっと違うだけです。
(Xenophy.controller.Header/Xenophy.view.Header)
同じ物に感じがちですが全く別物です。
Xenophy.controller.Headerはコントローラです。
それに対して、Xenophy.view.Headerはビューなのですが、イコール、コンポーネントです。
従来のクラスの感覚では、ビュー、イコール、コンポーネント、イコール、コントローラーだったわけですね。
コントローラーだけ切り離された、と考えましょう。

では、コントローラーで、controlメソッドを利用して、コンポーネントクエリーでターゲット指定をして、イベントハンドラを定義するのは既にやりました。
今度は、イベントハンドラ内で必要なコンポーネントを取得することを考えます。

ここでは、
ボタン1が押されたら、ナビゲーションパネル(Navi)を閉じる、開くのトグルボタンを作成する
ボタン2が押されたら、ヘッダーを非表示にする、3秒後に自動的に表示する
ということをやってみましょう。

まず、Xenophy.controller.Headerにrefsを設定します。

これを設定すると、コントローラに次のメソッドが自動的に生成されます。

  • getHeader
  • getNavi

これらのメソッドを利用して、ボタンイベントハンドラを実装してみると次のようになります。

ビュー、つまりコンポーネントにアクセスするためには、refsで設定したメソッドでアクセスします。
コンポーネントクエリーを使って、取得することも可能ですが、グローバル変数に物を入れてアクセスしているようなものなので、可能な限りrefsを利用します。

viewsについて

実は、ここまででXenophy.view.ViewportのusesにHeader, Navi, Centerを読み込むように指定していました。
Xenophy.controller.Mainのviewsに次のように指定すると、usesで指定しなくても実行できます。

考え方の問題ですが、こちらの方がいいかもしれません。
Mainコントローラーで使うViewたちは、これだ!っていうをviewsに指定する、っていう考えですね。
Ext JS 4 クラスシステムのusesやrequiresに指定しても、Ext.Loaderは実行されるので結果一緒です。

1点違う点があり、viewsに指定すると、自動的にクラス定義のオブジェクトを取得するために
get[ビュー名]Viewというメソッドが生成されます。

上記の場合だと、getHeaderViewgetCenterViewgetNaviViewというメソッドが実装されます。
先ほどのrefsと違う点があるのが注意点です。

なにも、「Headerコントローラーにviewsを指定すればいいじゃないか」と思うかもしれません。
しかし、先ほど書いたとおり「クラス定義」を取得するメソッドであり、「インタンス化されたコンポーネント」ではありません

なので、コンポーネントインタンスにアクセスして何かをするためには、refsで参照メソッドを作成しましょう。

Code7 to GitHub

最後に

さて、ここまでで、MVCの形をつくる基本的なクラス構成、ディレクトリ構成、そしてcontrolメソッドによるイベントハンドラの定義、コンポーネントへのアクセス方法を学びました。MVC形式には、これ以外にもmodel、storeに関しての定義方法があります。

長くなってきたので「Ext JS 4.0/4.1 MVCアプリケーション構造の理解II」で、
Naviにツリーを、TreeStoreを利用することで、model、storeを利用し、
さらに、Centerパネルの中にいくつかのパネルを作成して切り替える処理を実装します。
さらに、Naviのツリーと、Centerパネルの動作を連動させます。

お楽しみに。

Ext JS 4.0/4.1 MVCアプリケーション構造の理解 – 株式会社ゼノフィ社内勉強会資料

One thought on “Ext JS 4.0/4.1 MVCアプリケーション構造の理解 – 株式会社ゼノフィ社内勉強会資料

Comments are closed.