コンポーネント
Ext JS アプリケーションの UI は、一つ、または複数のコンポーネントというウィジェットで構築されています。全てのコンポーネントは Ext.Component クラスのサブクラスであり、インスタンス化、レンダリング、サイズと位置の調整、廃棄などの自動的なライフサイクル管理に参加するようになります。Ext JS はすぐに使える便利な幅広いコンポーネントを提供し、コンポーネントを拡張して、カスタマイズされたコンポーネントを生成することができます。
コンポーネントの階層
コンテナは他のコンポーネントを配置できる、特別なコンポーネントです。通常のアプリケーションは、コンポーネント階層と呼ばれるツリーのような構造でネストされたコンポーネントで作られています。コンテナは子コンポーネントのライフサイクルを管理し、そこには生成、レンダリング、サイズと位置変更、廃棄が含まれます。通常のアプリケーションのコンポーネント階層は、最上位が Viewport で始まり、ここにコンテナやコンポーネントが中にネストされます。
子コンポーネントは、コンテナの items コンフィグプロパティを使って、コンテナに追加されます。 この例では、 Ext.create() を使って二つの Panels, をインスタンス化して、その Panel を子コンポーネントとして Viewport に追加しています。
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 ] }); |
コンテナは、 Layout Manager レイアウトマネージャーを使って、子コンポーネントのサイズと位置を調整します。 レイアウトとコンテナについて、詳しくは、 Layouts and Containers Guide をご覧ください。
XTypes と 遅延インスタンス化
全てのコンポーネントには
xtype
というシンボル名があります。例えば、
Ext.panel.Panel
には panel
という xtype
があります。
全てのコンポーネントの xtype
は
Component の API ドキュメント
に掲載されています。
(訳注: 現在の API には一覧は掲載されていません。その代わり各コンポーネントのクラス名の横にそのコンポーネントの xtype
が載っています)
上のサンプルでは、
Container
に既にインスタンス化されたコンポーネント
Components
を追加する方法を示しました。
しかし、大きなアプリケーションでは、全てのコンポーネントをすぐにインスタンス化する必要がなく、アプリケーションの使い方によっては、一度もインスタンス化されないコンポーネントがあるかもしれないので、上記のサンプルは理想的ではありません。例えば、
Tab Panel
を使うアプリケーションは、ユーザーがタブをクリックした時だけに、各タブの内容をレンダリングする必要があります。コンテナが必要と判断するまではインスタンス化せずに、コンテナの子供が前もって構成する時に、xtype
が役に立ちます。
次のサンプルコードは、Tab Panel を利用して、コンテナの子コンポーネントの遅延インスタンス化とレンダリングを示します。 各タブは、タブがレンダリングされたらアラートを表示するイベントリスナーがあります。
Ext.create('Ext.tab.Panel', { renderTo: Ext.getBody(), height: 100, width: 200, items: [ { // 明示的にこのコンポーネントコンフィグで xtype を定義する // これはコンテナー(この場合はタブパネル)に、必要になったら // Ext.panel.Panel をインスタンス化するように指示しています 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.'); } } } ] }); |
このコードを実行すると、最初のタブですぐにアラートがでます。これはデフォルトでアクティブなタブなので、コンテナタブパネルはすぐにインスタンス化され、レンダリングされるためにそうなります。
タブがクリックされるまでは、二つ目のタブのアラートは表示されません。 render イベントはタブがアクティブになる前に発火しないので、必要になるまでレンダリングされないことを示しています。
表示と非表示
全ての Components には show と hide メソッドがあります。コンポーネントを非表示にするためのデフォルトCSSメソッドは “display: none” ですが、これは hideMode コンフィグで変更することができます。
var panel = Ext.create('Ext.panel.Panel', { renderTo: Ext.getBody(), title: 'Test', html: 'Test Panel', hideMode: 'visibility' // 表示の切替にCSSのvisibilityを使う }); 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, // 絶対座標のフローティングコンポーネントにする title: 'Test', html: 'Test Panel' }); |
上記のコードでは Panel をインスタンス化していますが、レンダリングはしません。通常はコンポーネントには renderTo コンフィグが指定されているか、 コンテナ Container の子コンポーネントとして追加されますが、フローティングコンポーネントの場合には、どちらも必要ではありません。フローティングコンポーネントは、 show メソッドが初めに呼び出されたら、ドキュメントボディに自動的にレンダリングされます。
panel.show(); // フローティングパネルを描画、表示する |
次はフローティングコンポーネントに関係する他のコンフィグやメソッドです。
- draggable – 画面上で浮動のコンポーネントをドラッグできるようになります
- shadow – フローティングコンポーネントの影の見た目をカスタマイズできます
- alignTo() – 浮動コンポーネントを指定したエレメントに揃える
- center() – 浮動コンポーネントをコンテナ内で真ん中に移動させる
カスタムコンポーネントの生成
作成、または拡張
新しい UI クラスを生成する時に、そのクラスが Component のインスタンスとするか、その Component を拡張するかを判断する必要があります。
最も近いベースクラスを必要な機能性に拡張するのを推奨します。その理由は、Ext JS が提供する自動化されたライフサイクル管理のためです。ライフサイクル管理では、必要になったら自動的にレンダリングされること、適切なレイアウトマネージャーで管理された時にはコンポーネントのサイズや位置を自動的に調整すること、 Container から取り除かれた時点で破棄されることなどの全てを提供します。
Ext JS の Componnet を所有する新しいクラスを作成してレンダリングや管理を外側からするよりも、Component 階層に配置できる Component である新しいクラスを作成する方がより簡単です。
サブクラス化
クラスシステムにより、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
は
Component.
に定義されてたメソッドです。コンポーネントライフサイクルのレンダリングのフェーズを起動する役割があります。render
はオーバーライドしてはいけませんが、クラス特定な処理を実行するために 、サブクラスを実装する人が onRender
メソッドを追加できるように、処理中に onRender を呼び出します。各 onRender
メソッドは、その追加のロジックを「提供」する前に、そのスーパークラスの onRender
メソッドを呼び出す必要があります。
下記の図は onRender
テンプレートメソッドの動作を示します。
render
メソッドが呼び出されます (これは Container のレイアウトマネージャーで行います)。このメソッドは Ext base クラスで実装され、オーバーライドしません。render
は現在のサブクラス内の実装となる this.onRender
を呼び出します (もし実装されていたら)。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
show 操作に動作を追加できます。スーパークラスのonShow
を呼び出したあと、コンポーネントが表示されます。afterShow
このメソッドはコンポーネントが表示された後に呼び出されます。onShowComplete
このメソッドはafterShow
メソッドが完了した後に呼び出されます。onHide
hide 操作に動作を追加できます。スーパークラスのonHide
を呼び出した後、コンポーネントが非表示になります。afterHide
このメソッドはコンポーネントが非表示になった後に呼び出されます。onRender
レンダリングフェーズに動作を追加できます。afterRender
レンダリングが完了した後に、動作を追加できます。この段階で、コンポーネントの要素は、コンフィグによってスタイリングされ、設定された CSSクラス名も追加され、設定された表示/非表示と、設定された有効/無効の状態になっています。onEnable
は enable 操作に動作を追加できます。スーパークラスのonEnable
を呼び出した後に、コンポーネントは有効されます。onDisable
は disable 操作に動作を追加できます。スーパークラスの onDisable を呼び出した後に、コンポーネントは無効にされます。onAdded
はコンテナにコンポーネントが追加された時の動作を追加できます。この段階で、コンポーネントは親コンテナの子アイテムのコレクションです。スーパークラスのonAdded
を呼び出した後には、ownerCt
への参照が存在しますので、ref コンフィグが設定されていたら、refOwner
がセットされます。onRemoved
はコンポーネントが親コンテナから取り外される時に動作を追加することができます。この時点で、コンポーネントは親コンテナの子アイテムのコレクションから削除されていますが、破棄されません (親コンテナの autoDestroy が true であるか、remove の呼び出し時の第二パラメータが true であれば破棄されます )。スーパークラスのonRemoved
を呼び出した後には、ownerCt
とrefOwner
は存在しません。onResize
は、サイズ変更の操作に動作を追加できます。onPosition
は、位置の操作に動作を追加することができます。onDestroy
は、destroy の操作に動作を追加できます。スーパークラスのonDestroy
を呼び出した後に、コンポーネントは破棄されます。beforeDestroy
このメソッドは、コンポーネントが破棄される前に呼び出されます。afterSetPosition
このメソッドは、コンポーネントの位置がセットされた後に呼び出されます。afterComponentLayout
このメソッドは、コンポーネントがレイアウトされた後に呼び出されます。beforeComponentLayout
このメソッドはコンポーネントがレイアウトされる前に呼び出されます。
どのクラスを拡張するのか
どのクラスを拡張するかは、主に効率とベースクラスが提供する必要がある機能性によります。どの UI コンポーネントのセットがレンダリングと管理される必要がある時は、いつも Ext.panel.Panel を拡張する傾向があります。 UI コンポーネントのセットを表示し管理するときには Ext.panel.Panel を拡張する傾向があります。
しかしパネルクラスにはたくさんの機能があります。
- ボーダー
- ヘッダー
- ヘッダーツール
- フッター
- フッターボタン
- 上部ツールバー
- 下部ツールバー
- 子コンポーネントの配置と管理
これらの機能が必要ではなければ、Panel を使うとリソースの無駄になります。
Component
必要なUI コンポーネントに、他のコンポーネントを配置する必要がない場合、単に必要な動作をするある形式の HTML をカプセル化するだけなら、
Ext.Component
が適切です。例えば、次のクラスは HTML の image 要素をラッピングするコンポーネントで、イメージの src
属性のセットとゲットができるようになります。また、イメージがロードされた後に load
イベントを発火します:
Ext.define('Ext.ux.Image', { extend: 'Ext.Component', // Ext.Component のサブクラス alias: 'widget.managedimage', // xtype は 'managedimage' autoEl: { tag: 'img', src: Ext.BLANK_IMAGE_URL, cls: 'my-managed-image' }, // onRender フェーズの独自処理を追加 // エレメントに'load'リスナーを追加 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
クラスでイメージを管理します。
Container
必要な UI コンポーネントには他のコンポーネントが配置されていて、以前説明した Panel, の追加の機能が必要ない場合には、 Ext.container.Container クラスを拡張しましょう。コンテナのレベルでは、どの Ext.layout.container.Container を使って子コンポーネントのレンダリングと管理をしているかを覚えておく事が重要です。
コンテナには次の追加のテンプレートメソッドがあります。
onBeforeAdd
このメソッドは新しい子コンポーネントを追加する前に呼び出されます。新しいコンポーネントが渡され、コンポーネントを変更したり、なんらかの方法でコンテナの準備に使用されるかもしれません。false を返すと操作が中断します。onAdd
このメソッドは新しいコンポーネントが追加された後に呼び出されます。追加されたコンポーネントが渡されます。このメソッドは、子アイテムの状態によって内部の構造をアップデートするために使用できます。onRemove
このメソッドは新しいコンポーネントが削除された後に呼び出されます。削除されたコンポーネントが渡されます。このメソッドは、子アイテムの状態によって内部の構造をアップデートするために使用できます。beforeLayout
このメソッドはコンテナが子コンポーネントをレイアウト (または、必要であればレンダリング) する前に呼び出されます。afterLayout
このメソッドはコンテナが子コンポーネントをレイアウト (または、必要であればレンダリング) した後に呼び出されます。
Panel
必要な UI コンポーネントにヘッダー、フッター、ツールバーなどが必要であれば、Ext.panel.Panel
を拡張します。
重要: Panle は Container です。どの Ext.layout.container.Container を使って子コンポーネントのレンダリングと管理をしているかを覚えておく事が重要です。
Ext.panel.Panel
を拡張したクラスは、普通はとてもアプリケーション特有なもので、通常は、指定したレイアウトで他の UI コンポーネント (通常はコンテナ、フォームフィールド) をまとめ、内包するコンポーネントを
tbar
と
bbar.
で操作する方法を提供します。
パネルには次の追加のテンプレートメソッドがあります:
afterCollapse
このメソッドは、パネルが折りたたんだ後に呼び出されます。afterExpand
このメソッドは、パネルが展開された後に呼び出されます。onDockedAdd
このメソッドは、パネルにドッキングアイテムが追加された後に呼び出されます。onDockedRemove
このメソッドは、パネルにドッキングアイテムが外された後に呼び出されます。