Ext JS 4のMVC Application Architectureを試してみた
先日ついにリリースされたExt JS 4に新たに追加されたMVCアーキテクチャを試してみました。
MVC Application Architecture
調べたことの簡単なまとめ
Ext JS 4のMVCアーキテクチャ
- モデル → Ext.data.Modelクラスを継承したクラス
- ビュー → パネル、グリッド、ツリーなどのコンポーネント
- コントローラー → Ext.app.Controllerクラスを継承したクラス
アプリケーションのディレクトリ構造
- appname - app - controller - model - store - view - resources - css - images - ... - app.js - index.html
appnameはアプリケーションの名前のディレクトリです。
アプリケーションはindex.htmlからロードするapp.jsで起動します。
このディレクトリ構造に沿うことで動的なJSファイルのロードや記述の簡略化などのフレームワークの恩恵を受けることができます。
index.html
<html> <head> <title>Contacts</title> <link rel="stylesheet" type="text/css" href="../ext-4.0.0/resources/css/ext-all.css" /> <link rel="stylesheet" type="text/css" href="resources/css/app.css" /> <script type="text/javascript" src="../ext-4.0.0/ext-debug.js"></script> <script type="text/javascript" src="app.js"></script> </head> <body></body> </html>
index.htmlからロードするのは
だけです。
その他のファイルはフレームワークがロードしてくれます。
app.js
Ext.application({ name: 'Contacts', appFolder: 'app', controllers: [ 'Contacts' ], launch: function() { Ext.create('Ext.container.Viewport', { layout: 'card', items: [ { xtype: 'contactlist' }, { xtype: 'contactnew' }, { xtype: 'contactedit' } ] }); } });
app.jsには
- nameにアプリケーションの名前を定義します。
- appFolderにアプリケーションの格納フォルダ(デフォルト:app)を指定します。
- controllerにアプリケーションのコントローラー(複数可)を指定します。
- launchに起動ファンクションを定義します。
Controller
Ext.define('Contacts.controller.Contacts', { extend: 'Ext.app.Controller', requires: [ 'Ext.layout.container.Card', 'Ext.layout.container.Border', 'Ext.data.proxy.Rest', 'Ext.form.field.Hidden' ], views: [ 'contact.List', 'contact.Grid', 'contact.New', 'contact.Edit', 'contact.Form' ], stores: [ 'Contacts' ], models: [ 'Contact' ], init: function() { console.log('controller init'); this.control({ 'contactgrid > toolbar > button[action=new]': { click: function(button) { button.up('viewport').getLayout().setActiveItem(1); } }, //snip }) } })
Controllerに定義することはモリモリあります。
- requiresで必要なレイアウトなどを指定します(これはいいのか不明)。
- viewsでアプリのビューを指定します。
- storesでアプリで使うStoreを指定します。
- modelsでアプリのモデルを指定します。
- initファンクションはアプリの起動前に呼び出されます。
- controlファンクションにはビューで発生するイベントのリスナーを定義できます。
イベントが発生するコンポーネントは
'contactgrid > toolbar > button[action=new]'
です。
CSSセレクタ風にExt JSのコンポーネントを指定できます。
このコンポーネントのclickイベントをリスンしています。
View
Ext.define('Contacts.view.contact.List', { extend: 'Ext.Panel', alias: 'widget.contactlist', layout: 'border', defaults: { border: false }, initComponent: function() { var me = this; Ext.apply(me, { items: [{ region: 'north', height: 50 }, { region: 'west', width: 200 }, { region: 'south', height: 200 }, { region: 'center', items: [ {xtype: 'contactgrid'} ] }] }); this.callParent(arguments); } });
ViewはExt JSのコンポーネントです。
基本的にExt JS3のときと同じように書けます。
明らかに変わったのは、xtypeの定義とinitComponentファンクションからのスーパークラスをコールするファンクションでしょうか。
xtypeは下記のように定義します。
alias: 'widget.contactlist',
widgetは必須のprefixです。他からこのコンポーネントをxtypeで指定するときは
xtype: 'contactlist'
になります。
スーパークラスをコールするファンクションはcallParentファンクションに変わっています。
ModelとStore
Ext.define('Contacts.model.Contact', { extend: 'Ext.data.Model', fields: [ "_docId", "givenName", "familyName", "emails", "phoneNumbers", ], proxy: { type: 'rest', url: '/_je/myDoc' } });
Modelはproxyを持ってサーバー通信したり、他のモデルとhas_manyなどの関連をもったり、バリデーションの機能があったりするようですが未検証です。
Ext.define('Contacts.store.Contacts', { extend: 'Ext.data.Store', model: 'Contact', fields: [ "_docId", "givenName", "familyName", "emails", "phoneNumbers", ], proxy: { type: 'rest', url: '/_je/myDoc' }, autoLoad: true });
StoreはModelの集約として使えます。Ext JS3からかなり変わったみたいですが未検証。
Ext JS4のExt.define
クラス設定値のオブジェクトには下記の設定を指定できます。(デフォルト)
ExtJS4のクラス定義まわり - S5
さっそく間違えてました。
requiresとusesが漏れてました。
(Beta2で確認しました)
Ext.define('My.awesome.Hoge',{ requires: [ 'Ext.panel.Panel', 'Ext.button.Button' ], uses: [ 'Ext.grid.GridPanel' ] );
requiresにはロードするクラスを配列で定義します。
クラス名からロードするJSファイルを探して、非同期でロードする...ようです。
usesも同じようにクラスのロードのはずですが、ざっとみたところでは整理できてないです。
ExtJS4のクラス定義まわり
※ここの内容はExtJS4 beta1で確認しました。正式リリース版では変わるかもしれません
Ext.define
Ext JS4でクラスを定義するにはExt.defineをコールします。
Ext.defineは3つの引数を取ります。
- (パッケージ名含む)クラス名の文字列
- クラス設定値のオブジェクト
- クラスを定義したときのコールバック関数
クラス設定値のオブジェクトには下記の設定を指定できます。(デフォルト)
- extend
- スーパークラスを指定します
- mixins
- config
- クラスのインスタンス属性を定義します。属性ごとにsetter/getter/apply/resetのメソッドが提供されます
- statics
- クラスのスタティック属性を定義します
- alias
- クラスのxtypeを定義します
- singleton
- クラスをシングルトンにします
- alternateClassName
- クラス名の別名を定義します
Ext.define('My.awesome.Foo1',{ foo1: function(){ console.log("this is foo1"); } }); Ext.define('My.awesome.Foo2',{ foo2: function(){ console.log("this is foo2"); } }); Ext.define('My.awesome.Bar',{ }); Ext.define('My.awesome.Hoge',{ //My.awesome.Barクラスを継承します extend: 'My.awesome.Bar', //My.awesome.Foo1クラスのfoo1メソッドとMy.awesome.Foo2のfoo2メソッドをこのクラスのインスタンスメソッドにします mixins: { foo1: My.awesome.Foo1, foo2: My.awesome.Foo2 }, //属性「fuga」と「piyo」を定義します config: { fuga: 100, piyo: "hello" }, //クラスのスタティック属性を定義します statics: { say: function(){ console.log("this is static"); } }, //コンストラクタ constructor: function(config) { this.initConfig(config); return this; } }, function(cls){ console.log('クラスを定義したときのコールバックです'); });
Ext.create
クラスをインスタンス化するにはExt.createをコールします。
Ext.createは2つの引数を取ります。
- (パッケージ名含む)クラス名の文字列
- 引数のオブジェクト
var hoge = Ext.create('My.awesome.Hoge',{ fuga: 200 }); hoge.foo1(); hoge.foo2(); console.log(hoge.getFuga()); console.log(hoge.getPiyo()); My.awesome.Hoge.say();
実行結果
//クラスを定義したときのコールバック関数 クラスを定義したときのコールバックです //mixinsで取り込んだメソッド this is foo1 this is foo2 //インスタンス属性 200 hello //スタティック属性 this is static
進撃の巨人を読みました
[book]「このマンガがすごい2010」で1位をとった進撃の巨人を読みました。
Amazonのレビューで書き尽くされている感じがありますが、面白いです。読み始めると入り込みます。作者の書きたくてしょうがない感が伝わってくるようなアツい作品です。
- 作者: 諫山創
- 出版社/メーカー: 講談社
- 発売日: 2010/03/17
- メディア: コミック
- 購入: 35人 クリック: 1,257回
- この商品を含むブログ (381件) を見る
- 作者: 諫山創
- 出版社/メーカー: 講談社
- 発売日: 2010/07/16
- メディア: コミック
- 購入: 28人 クリック: 417回
- この商品を含むブログ (191件) を見る
- 作者: 諫山創
- 出版社/メーカー: 講談社
- 発売日: 2010/12/09
- メディア: コミック
- 購入: 23人 クリック: 786回
- この商品を含むブログ (168件) を見る
MacBook Air 11インチ欲しい!
MacBook Air 11インチ欲しい!
MacBook Air 11インチ欲しい!
大事なことなので2回言いました
ブログの移行
これからはこっちに書こうと思います。
http://d.hatena.ne.jp/s5ot/