Ext JSアプリケーションのUIは、コンポーネントと呼ばれる1つ以上のウィジェットで構成されています。すべてのコンポーネントは、インスタンス化、レンダリング、サイズや位置の決定、破棄などの自動化されたライフサイクル管理の機能を可能にする Ext.Component クラスのサブクラスです。Ext JSには、広範囲の便利なコンポーネントが用意されています。どのコンポーネントも簡単に継承してカスタマイズコンポーネントを作成することができます。
コンポーネント階層
コンテナは、他のコンポーネントを格納できる特殊なタイプのコンポーネントです。典型的なアプリケーションは、コンポーネント階層と呼ばれるツリー状の構造を持ったたくさんのコンポーネントの集合体から構成されています。コンテナは、作成、レンダリング、サイズ決めと位置決め、破棄など、子のコンポーネントライフサイクルの管理を担当しています。一般的なアプリケーションのコンポーネント階層は、上位のビューポート(その中にネストされた他のコンテナおよび/またはコンポーネントを含む)から始まります。
子コンポーネントは、コンテナのitemsコンフィグプロパティを使用してコンテナに追加します。この例では、Ext.create() を使用してパネルのインスタンスを2つ作成してから、ビューポートの子コンポーネントとして追加しています。
var childPanel1 = Ext.create('Ext.panel.Panel', {
title: 'Child Panel 1',
html: 'A Panel'
});
var childPanel2 = Ext.create('Ext.panel.Panel', {
title: 'Child Panel 2',
html: 'Another Panel'
});
Ext.create('Ext.container.Viewport', {
items: [ childPanel1, childPanel2 ]
});
コンテナは、レイアウトマネージャーを使用して、子コンポーネントのサイズと位置を決定します。レイアウトとコンテナに関する詳細は、レイアウトとコンテナガイドを参照してください。
XTypeと遅延インスタンス化
どのコンポーネントにも xtype と呼ばれるシンボリック名があります。例えば、Ext.panel.Panel には、‘panel’ の xtype
があります。上記の例では、既にインスタンス化していたコンポーネントをコンテナに追加する方法を示しました。しかし、大規模なアプリケーションで、コンポーネント(全てではない)がすぐにインスタンス化される必要があるならば、これは理想的ではなく、いくつかのコンポーネントはアプリケーションの使い方次第でインスタンス化されないかもしれません。例えば、タブパネルを使うアプリケーションはそれぞれのタブがユーザーによってクリックされると、レンダリングするそれぞれのタブのコンテンツだけが必要です。これが、コンテナの子に前もって設定されることによるxtype
の便利な部分ですが、コンテナが必要であると判断するまでは、インスタンス化されません。
次のコード例では、タブパネルを使ったコンテナの子コンポーネントのゆっくりしたインスタンス化とレンダリングを説明しています。各タブには、タブがレンダリングされた時のアラートを表示するイベントリスナがあります。
Ext.create('Ext.tab.Panel', {
renderTo: Ext.getBody(),
height: 100,
width: 200,
items: [
{
// Explicitly define the xtype of this Component configuration.
// This tells the Container (the tab panel in this case)
// to instantiate a Ext.panel.Panel when it deems necessary
xtype: 'panel',
title: 'Tab One',
html: 'The first tab',
listeners: {
render: function() {
Ext.MessageBox.alert('Rendered One', 'Tab One was rendered.');
}
}
},
{
// xtype for all Component configurations in a Container
title: 'Tab Two',
html: 'The second tab',
listeners: {
render: function() {
Ext.MessageBox.alert('Rendered One', 'Tab Two was rendered.');
}
}
}
]
});
このコードを実行することにより、最初のタブの緊急アラートの結果がでます。これは、デフォルトのアクティブなタブなので発生し、そのため、すぐにそのコンテナのタブパネルをインスタンス化してレンダリングします。
タブがクリックされるまで、2番目のタブのアラートが表示されません。これは、タブがアクティブになるまで render イベントが発火しなかったため、必要になるまでタブがレンダリングされなかったことを示します。
表示および非表示
全てのコンポーネントには、showメソッドとhideメソッドがビルトインされています。コンポーネントを非表示にするために使用されるデフォルトのCSSメソッドは、“display: none”ですが、これは、hideMode設定を使用して変更できます。
var panel = Ext.create('Ext.panel.Panel', {
renderTo: Ext.getBody(),
title: 'Test',
html: 'Test Panel',
hideMode: 'visibility' // use the CSS visibility property to show and hide this
component
});
panel.hide(); // hide the component
panel.show(); // show the component
フローティングコンポーネント
フローティングコンポーネントは、CSSの絶対位置を使用するドキュメントフローの外側に位置し、コンテナのレイアウトには関係しません。Windowのようなコンポーネントは、デフォルトでフローティングしますが、floatingコンフィグを使用すると、どのコンポーネントでもフローティング可能です。
var panel = Ext.create('Ext.panel.Panel', {
width: 200,
height: 100,
floating: true, // make this panel an absolutely-positioned floating component
title: 'Test',
html: 'Test Panel'
});
上記のコードは、Panelをインスタンス化しますが、レンダリングしません。通常、コンポーネントは、renderTo 設定が指定されているか、Container の子コンポーネントとして追加されているかのどちらかですが、フローティングコンポーネントの場合はどちらも必要としません。フローティングコンポーネントは、最初にshowメソッドが呼び出されると、ドキュメントボディに自動的にレンダリングされます。
panel.show(); // render and show the floating panel
フローティングコンポーネントに関連して覚えておく、いくつかの他の設定とメソッドになります。
- draggable - 画面でフローティングコンポーネントをドラッグすることを可能にします。
- shadow - フローティングコンポーネントのシャドウの表示をカスタマイズします。
- alignTo() - フローティングコンポーネントを特定の要素に揃えて整列します。
- center() - フローティングコンポーネントをコンテナの中央に配置します。
カスタムコンポーネントの作成
内包か継承か
新しいUIクラスを作成する場合、そのクラスがコンポーネントのインスタンスを所有するか、またはそのコンポーネントを継承するかを決定する必要があります。
要求される機能に最も近いベースクラスに継承することをお勧めします。その理由は、必要に応じて自動レンダリング、適切なレイアウトマネージャーが管理する場合にはコンポーネントの自動的なサイズや位置の決定、コンテナから削除時に自動破棄など、Ext JSがライフサイクルを自動的に管理しているためです。
Ext JSのコンポーネントを有する新たなクラスよりもコンポーネント階層の中で場所を確保することができ、そのうえ外部からのレンダリングと管理の必要な新しいクラスを作成する方が簡単です。
サブクラス化
クラスシステムを使用することによって、Ext JS フレームワークの一部を簡単に拡張できます。
Ext.Base はすべての Ext JS クラスの構成要素で、このクラスのプロトタイプや静的メンバーは他のすべてのクラスによって継承されます。
Ext.Base で最下位の機能を追加できますが、多くの場合に開発者は継承チェーンのより高位から開始したいと考えます。
以下の例では、Ext.Component のサブクラスを作成します。
Ext.define('My.custom.Component', {
extend: 'Ext.Component',
newMethod : function() {
//...
}
});
この例では、定義された新しいメソッドまたはプロパティに加えて、Ext.Component のすべての機能(メソッド、プロパティなど)を継承する新しいクラス My.custom.Component を作成します。
テンプレートメソッド
Ext JSは、サブクラスとそのサブクラス特有の振る舞いを任命するためにテンプレートメソッドパターンを使用します。
これは、継承関係上のそれぞれのクラスがコンポーネントのライフサイクルで特定の段階に対する別部分のロジックにおいて「貢献」する可能性があることを意味します。継承関係内の他のクラスがその独自のロジックに“貢献”することを許可している一方、各クラスは独自の動作を実装します。
一例は、render関数です。render
は Component で定義されるメソッドです。コンポーネントのライフサイクルのレンダリング段階を開始します。render
をオーバーライドすることはできませんが、クラス固有の処理を実行する onRender
メソッドを追加するために、サブクラス実装者の許可を処理する間に onRender
を呼び出します。すべての onRender
メソッドは、別ロジックで「貢献」する前に、そのスーパークラスの onRender
メソッドを呼び出さなければなりません。
下の図は、onRender
テンプレートメソッドの機能を示しています。
render
メソッドが呼び出されます(コンテナのレイアウトマネージャーによって実行)。このメソッドはオーバーライドされない可能性もあり、Extベースクラスによって実行されます。現在のサブクラス内に実装されたthis.onRender
を呼び出します(実装されている場合)。これが、スーパークラスバージョンを呼び出すスーパークラスバージョンなどを呼び出します。 ついには、それぞれのクラスはその機能に貢献し、Render
機能戻りを制御します。
onRender
メソッドを実装するコンポーネントのサブクラスの例は次のとおりです。
Ext.define('My.custom.Component', {
extend: 'Ext.Component',
onRender: function() {
this.callParent(arguments); // call the superclass onRender method
// perform additional rendering tasks here.
}
});
テンプレートメソッドの多くは、対応するイベントも持つことを覚えておくことが重要です。例えば、コンポーネントがレンダリングされた後にrenderイベントが発火されます。しかしながら、サブクラス化した時、イベントでは*ない*ライフサイクルで重要なフェーズにおいて、クラスロジックを実行するためのテンプレートメソッドの使用は必要不可欠です。イベントはプログラム的に停止、もしくはハンドラによって停止することができます。
下記は、コンポーネントのサブクラスで実装できるテンプレートメソッドです。
initComponent
このメソッドは、コンストラクタによって起動されます。データの初期化、構成の設定、イベントハンドラの追加の際に使用されます。beforeShow
このメソッドは、コンポーネントが表示される前に起動されます。onShow
表示操作に動作を追加できるようにします。親クラスのonShow呼び出しの後でコンポーネントが表示になります。afterShow
このメソッドは、コンポーネントが表示された後に起動します。onShowComplete
このメソッドは、afterShow
メソッドが完了した後に起動します。onHide
非表示操作に動作を追加できるようにします。スーパークラスの onHide を呼び出した後、コンポーネントは非表示になります。afterHide
このメソッドは、コンポーネントが非表示になった後、起動します。onRender
レンダリングフェーズに動作を追加できるようにします。afterRender
レンダリングが完了した後に動作を追加できるようになります。この段階ではコンポーネントの要素は、設定に応じてスタイルが設定されているはずで、設定済みの CSS クラス名が追加され、その可視性と enable 状態も設定済みとなります。onEnable
有効化操作に動作を追加できるようにします。スーパークラスの onEnable を呼び出した後、コンポーネントが有効になります。onDisable
無効化操作に動作を追加できるようにします。スーパークラスの onDisable を呼び出した後、コンポーネントは無効になります。onAdded
コンポーネントがコンテナに追加されると、動作を追加できるようにします。この段階では、コンポーネントは、その親コンテナの子アイテムのコレクションにあります。スーパークラスの onAdded を呼び出した後、ownerCt リファレンスが存在するようになります。ref が設定されている場合、refOwner が設定されます。onRemoved
コンポーネントがその親コンテナから削除されると、動作を追加できるようにします。この段階で、コンポーネントは、その親コンテナの子アイテムのコレクションから削除されていますが、まだ破棄されていません(親コンテナのautoDestroyがtrueまたは、removeの呼び出しで2番目のパラメータに真の値が渡された場合は破棄されます)。スーパークラスのonRemovedを呼び出した後、ownerCtとrefOwnerは存在しなくなります。onResize
サイズ変更操作に動作を追加できるようにします。onPosition
位置操作に動作を追加できるようにします。onDestroy
破壊操作に動作を追加できるようにします。スーパークラスのonDestroyを呼び出した後、コンポーネントが破棄されます。beforeDestroy
このメソッドは、コンポーネントが破棄される前に起動します。afterSetPosition
このメソッドは、コンポーネントの位置が設定された後、起動します。afterComponentLayout
このメソッドは、コンポーネントが配置された後に起動します。beforeComponentLayout
このメソッドは、コンポーネントが配置される前に起動します。
継承するクラス
継承する最良のクラスの選択は、効率性とどの機能をベースクラスが提供しなければならないのかが主な問題です。UI コンポーネントのセットにレンダリングと管理が必要な場合、 Ext.panel.Panel を常に継承する傾向がありました。
Panelクラスは、多くの機能を備えています:
- Border
- Header
- Headerツール
- Footer
- Footerボタン
- Topツールバー
- Bottomツールバー
- 子コンポーネントの格納と管理
これらが必要ない場合は、パネルの使用はリソースの無駄となります。
コンポーネント
必要な UI コンポーネントが他のコンポーネントを必要としない場合、言い換えるなら要件を実行する一部のフォームの HTML をカプセル化する場合、Ext.Component の継承は適切です。例えば、次のクラスは、HTMLイメージ要素を包み込むコンポーネントであり、設定動作の追加やイメージのsrc
属性を取得できます。また、画像がロードされたとき、load
イベントを発生させます:
Ext.define('Ext.ux.Image', {
extend: 'Ext.Component', // subclass Ext.Component
alias: 'widget.managedimage', // this component will have an xtype of 'managedimage'
autoEl: {
tag: 'img',
src: Ext.BLANK_IMAGE_URL,
cls: 'my-managed-image'
},
// Add custom processing to the onRender phase.
// Add a 'load' listener to the element.
onRender: function() {
this.autoEl = Ext.apply({}, this.initialConfig, this.autoEl);
this.callParent(arguments);
this.el.on('load', this.onLoad, this);
},
onLoad: function() {
this.fireEvent('load', this);
},
setSrc: function(src) {
if (this.rendered) {
this.el.dom.src = src;
} else {
this.src = src;
}
},
getSrc: function(src) {
return this.el.dom.src || this.src;
}
});
使用例:
var image = Ext.create('Ext.ux.Image');
Ext.create('Ext.panel.Panel', {
title: 'Image Panel',
height: 200,
renderTo: Ext.getBody(),
items: [ image ]
});
image.on('load', function() {
console.log('image loaded: ', image.getSrc());
});
image.setSrc('http://www.sencha.com/img/sencha-large.png');
この例はデモンストレーションのみを目的としています。実際のアプリケーションでは Ext.Img クラスで画像を管理してください。
コンテナ
必要な UI コンポーネントが他のコンポーネント含んでいて、前述した Panel の追加機能を必要としない場合、Ext.container.Container は、拡張に適したクラスです。コンテナレベルでは、どの Ext.layout.container.Container が子コンポーネントのレンダリングと管理に使用されるかを覚えておくことが重要です。
コンテナには、以下の追加テンプレートのメソッドがあります:
onBeforeAdd
このメソッドは、新しい子コンポーネントを追加する前に起動します。新しいコンポーネントを渡され、コンポーネントを変更する、または何らかの方法でコンテナを準備するために使用される場合があります。falseを返すと、追加操作を中止します。onAdd
このメソッドは、新しいコンポーネントが追加された後に起動します。追加されたコンポーネントに渡されます。このメソッドは、子アイテムの状態に依存する場合がある内部構造を更新するために使用することがあります。onRemove
このメソッドは、新しいコンポーネントが削除された後、起動します。削除されたコンポーネントに渡されます。このメソッドは、子アイテムの状態に依存する場合がある内部構造を更新するために使用することがあります。beforeLayout
このメソッドは、コンテナがその子コンポーネントをレイアウト(さらに必要に応じてレンダリング)する前に起動します。afterLayout
コンテナがその子コンポーネントをレイアウト(必要に応じてレンダリング)された後に、このメソッドが呼び出されます。
パネル
要求されたUIコンポーネントは、ヘッダー、フッター、またはツールバーを使用している必要があり、Ext.panel.Panel は継承に適したクラスです。
重要:パネルはコンテナです。子コンポーネントのレンダリングと管理に使用するLayoutを覚えておくことが重要です。
Ext.panel.Panel を継承したクラスは、通常、非常にアプリケーション特有で、 一般的に、設定されたレイアウトの他の UI コンポーネント(通常はコンテナ、またはフォームフィールド)を集約するために使用され、tbar や bbar のコントロールによって格納されたコンポーネントを操作する手段を提供します。
パネルには、以下の追加テンプレートのメソッドがあります:
afterCollapse
このメソッドは、パネルが折りたたまれた後起動します。afterExpand
このメソッドは、パネルが展開された後に起動します。onDockedAdd
このメソッドは、ドッキングしたアイテムがパネルに追加された後に起動します。onDockedRemove
このメソッドは、ドッキングしたアイテムがパネルから削除された後に起動します。