(ExtJS6.5)associatedDataコンフィグ[modern]
ExtJS6.5で、Ext.dataview.AbstractクラスにassociatedDataコンフィグが追加されました。
Ext.dataview.Abstractは、リストやグリッドの基底クラスです。
リストやグリッドのようなデータビューのレンダリング処理では、ストアのモデルのデータをitemTplコンフィグで指定されたテンプレート内の変数に埋め込みます。
その際、アソシエーション付けしたフィールドがあると、レンダリング処理の中でそのデータも全て取得しようとします。
はじめから表示する項目にアソシエーションのデータが含まれないのであれば、それを除外できたほうがパフォーマンス上有利です。
associatedDataコンフィグを使うとその設定を実現できます。
モデルとストア
Ext.define('Sample.model.Address', { extend: 'Ext.data.Model', fields: [ { name: 'state', type: 'string' }, { name: 'cities', type: 'string' } ] }); Ext.define('Sample.model.User', { extend: 'Ext.data.Model', requires: [ 'Sample.model.Address' ], fields: [ { name: 'id', type: 'int' }, { name: 'name', type: 'string' }, { name: 'tel', type: 'string' }, { name: 'age', type: 'int' }, { name: 'address', reference: 'Sample.model.Address' } ] }); Ext.define('Sample.store.User', { extend: 'Ext.data.Store', requires: [ 'Sample.model.User' ], model: 'Sample.model.User', proxy: 'memory', data: [ {id: 1, name: 'yamada1', tel: '09011111111', age: 35, address: {state: '鹿児島県', cities: '鹿児島市1'}}, {id: 2, name: 'yamada2', tel: '09022222222', age: 28, address: {state: '鹿児島県', cities: '鹿児島市2'}}, {id: 3, name: 'yamada3', tel: '09033333333', age: 24, address: {state: '鹿児島県', cities: '鹿児島市3'}}, {id: 4, name: 'yamada4', tel: '09044444444', age: 20, address: {state: '鹿児島県', cities: '鹿児島市4'}}, {id: 5, name: 'yamada5', tel: '09055555555', age: 62, address: {state: '熊本県', cities: '熊本市1'}}, {id: 6, name: 'yamada6', tel: '09066666666', age: 61, address: {state: '熊本県', cities: '熊本市2'}}, {id: 7, name: 'yamada7', tel: '09077777777', age: 40, address: {state: '熊本県', cities: '熊本市3'}}, {id: 8, name: 'yamada8', tel: '09088888888', age: 38, address: {state: '熊本県', cities: '熊本市4'}}, {id: 9, name: 'yamada9', tel: '09099999999', age: 32, address: {state: '熊本県', cities: '熊本市5'}} ] }); Ext.define('Sample.view.main.Main', { extend: 'Ext.Panel', xtype: 'app-main', requires: [ 'Ext.layout.Fit', 'Ext.dataview.List', 'Sample.store.User' ], controller: 'main', layout: 'fit', title: 'ユーザ一覧', items: { xtype: 'list', store: 'User', itemTpl: [ '{name}<br>{address.state}{address.cities}' ] } });
特に何も設定していない場合は、↑となります。addressがアソシエーションの項目です。
associatedData: falseを指定すると、全てのアソシエーションを除外します。
items: { xtype: 'list', store: 'User', itemTpl: [ '{name}<br>{address.state}{address.cities}' ], associatedData: false }
XTemplate evaluation exception: Cannot read property 'state' of undefined となり、addressがundefinedになっていることがわかります。
特定の項目だけ取得したいときは↓のようにするようです。
associatedData: { address: true }
ちなみに、address: falseでも同じ結果になりました。バグっぽいね。
(ExtJS6.5)リストの新機能[modern]
今回はリストの追加機能を取り上げます。
行のスワイプ操作
Ext.dataview.listswiper.ListSwiperというプラグインクラスを使って、リストの行をスワイプするアクションが簡単に実装できるようになりました。
Ext.define('Sample.view.main.Main', { extend: 'Ext.Panel', xtype: 'app-main', requires: [ 'Ext.layout.Fit', 'Ext.dataview.List', 'Ext.dataview.listswiper.ListSwiper' ], controller: 'main', layout: 'fit', title: 'ユーザ一覧', items: { xtype: 'list', store: { proxy: 'memory', fields: [ 'id', 'state', 'name', 'tel', 'age' ], data: [ { id: 1, state: '鹿児島県', name: 'yamada1', tel: '09011111111', age: 35 }, { id: 2, state: '鹿児島県', name: 'yamada2', tel: '09022222222', age: 28 }, { id: 3, state: '鹿児島県', name: 'yamada3', tel: '09033333333', age: 24 }, { id: 4, state: '鹿児島県', name: 'yamada4', tel: '09044444444', age: 20 }, { id: 5, state: '熊本県', name: 'yamada5', tel: '09055555555', age: 62 }, { id: 6, state: '熊本県', name: 'yamada6', tel: '09066666666', age: 61 }, { id: 7, state: '熊本県', name: 'yamada7', tel: '09077777777', age: 40 }, { id: 8, state: '熊本県', name: 'yamada8', tel: '09088888888', age: 38 }, { id: 9, state: '熊本県', name: 'yamada9', tel: '09099999999', age: 32 } ] }, itemTpl: [ '{name}' ], plugins: { listswiper: { defaults: { width: 48 }, right: [{ iconCls: 'x-fa fa-envelope', ui: 'alt confirm', commit: 'onMessage' }, { iconCls: 'x-fa fa-phone', ui: 'alt action', commit: 'onCall' }, { iconCls: 'x-fa fa-trash', ui: 'alt decline', commit: 'onDeleteItem', undoable: true }] } } } });
行をスワイプすると、右側からボタンが現れます。いいですね。こういうのを見ると、すぐ導入したくなっちゃいます。こういう機能追加は大歓迎です。
さらにExt.dataview.listswiper.Stepperを使って、もう少し違う形式にもなります。
plugins: { listswiper: { widget: { xtype: 'listswiperstepper' }, defaults: { width: 48 }, right: [{ iconCls: 'x-fa fa-envelope', ui: 'alt confirm', commit: 'onMessage' }, { iconCls: 'x-fa fa-phone', ui: 'alt action', commit: 'onCall' }, { iconCls: 'x-fa fa-trash', ui: 'alt decline', commit: 'onDeleteItem', undoable: true }] } }
重複するのでpluginsコンフィグだけ抜粋です。
これは、スワイプ途中で離すとアクションが発生するようです。画像2枚目の状態で指を離すと、onMessageメソッドが実行されるようになっています。
なんかすごいですね。AndroidやiOSのAPIにはあまり詳しくないですが、こういう機能が標準搭載されているのでしょうか?
一応グリッドでも使えるみたいですね。横スクロールがある場合、あんまりよろしくないようですが。
Pull Refresh
リストが一番上にある状態で、さらにスクロールさせることで最新データをリロードさせる操作ですね。
Ext.dataview.pullrefresh.PullRefreshプラグインクラスを使います。
ExtJS6.2だと、ブラウザの標準機能に影響を受けて正しく機能しなかったため全然使ってませんでしたが、今回は大丈夫そうです。スマフォで試してみても動作しているようでした。
Ext.define('Sample.view.main.Main', { extend: 'Ext.Panel', xtype: 'app-main', requires: [ 'Ext.layout.Fit', 'Ext.dataview.List', 'Ext.dataview.pullrefresh.PullRefresh' ], controller: 'main', layout: 'fit', title: 'ユーザ一覧', items: { xtype: 'list', store: { proxy: 'memory', fields: [ 'id', 'state', 'name', 'tel', 'age' ], data: [ {id: 1, state: '鹿児島県', name: 'yamada1', tel: '09011111111', age: 35}, {id: 2, state: '鹿児島県', name: 'yamada2', tel: '09022222222', age: 28}, {id: 3, state: '鹿児島県', name: 'yamada3', tel: '09033333333', age: 24}, {id: 4, state: '鹿児島県', name: 'yamada4', tel: '09044444444', age: 20}, {id: 5, state: '熊本県', name: 'yamada5', tel: '09055555555', age: 62}, {id: 6, state: '熊本県', name: 'yamada6', tel: '09066666666', age: 61}, {id: 7, state: '熊本県', name: 'yamada7', tel: '09077777777', age: 40}, {id: 8, state: '熊本県', name: 'yamada8', tel: '09088888888', age: 38}, {id: 9, state: '熊本県', name: 'yamada9', tel: '09099999999', age: 32} ] }, itemTpl: [ '{name}' ], plugins: { pullrefresh: true } } });
pullrefresh: trueを指定すると、↓のようにpullrefreshspinnerを使うようです。
plugins: { pullrefresh: { widget: 'pullrefreshspinner', overlay: true } }
ほかにもpullrefreshbarを使う方法もあるようです。
plugins: { pullrefresh: { widget: 'pullrefreshbar', overlay: false } }
たしか元々のUIはこれでしたね。スピナーのほうは無かったと思います。
(ExtJS6.5)グリッドの新機能[modern] (3)
今回もグリッドについてです。細かいところばかりなので、前回書いとけば良かったです。
Virtual Stores
Ext.data.virtual.Storeというクラスが追加されたようです。
BufferedStoreと同じようなクラスであるとの記述があります。
管理するデータセットのうち必要なレコードおよびその前後のレコードをロード・保持できるようです。
たしかにBufferedStoreと同じような気がしますが、残念ながら私自身は具体的な利用箇所をイメージできていません。
いくつか違いがあるようですが、そのうち「複数のビューで共有可能」という点があるので、VirtualStoreのほうが応用効くのかな。(そもそもBufferedStoreが共有できないことを知らなかったですが)
もう少し情報を仕入れてから判断したいところです。
選択用クラス名が変わってる
名前変わっただけじゃなくて、いくつか機能も追加されているようです。
Ext.grid.plugin.MultiSelection -> Ext.grid.plugin.RowOperations
グルーピング
グリッドのgroupHeaderコンフィグでテンプレートなどを指定するようになりました。
前回しれっとグルーピングしているので、具体的なコードはそちらで確認できます。
http://sham-memo.hatenablog.com/entry/2017/06/02/200247
グリッドのサマリー
Ext.grid.SummaryRowを使いますが、groupFooterかpinnedFooterコンフィグに指定するようになりました。
年齢を追加して、平均年齢を表示させてみました。
Ext.define('Sample.view.main.Main', { extend: 'Ext.Panel', xtype: 'app-main', requires: [ 'Ext.layout.Fit' ], layout: 'fit', title: 'ユーザ一覧', items: { xtype: 'grid', store: { proxy: 'memory', fields: [ 'id', 'state', 'name', 'tel', { name: 'age', summary: 'average' } ], data: [ { id: 1, state: '鹿児島県', name: 'yamada1', tel: '09011111111', age: 35 }, { id: 2, state: '鹿児島県', name: 'yamada2', tel: '09022222222', age: 28 }, { id: 3, state: '鹿児島県', name: 'yamada3', tel: '09033333333', age: 24 }, { id: 4, state: '鹿児島県', name: 'yamada4', tel: '09044444444', age: 20 }, { id: 5, state: '熊本県', name: 'yamada5', tel: '09055555555', age: 62 }, { id: 6, state: '熊本県', name: 'yamada6', tel: '09066666666', age: 61 }, { id: 7, state: '熊本県', name: 'yamada7', tel: '09077777777', age: 40 }, { id: 8, state: '熊本県', name: 'yamada8', tel: '09088888888', age: 38 }, { id: 9, state: '熊本県', name: 'yamada9', tel: '09099999999', age: 32 } ], grouper: { property: 'state' } }, grouped: true, groupHeader: { tpl: 'Group: {name} ({count})' }, groupFooter: { xtype: 'gridsummaryrow', cls: 'user-summaryrow' }, columns: [ { dataIndex: 'id', text: 'ID', align: 'right' }, { dataIndex: 'name', text: '氏名', minWidth: 150, flex: 1 }, { dataIndex: 'tel', text: '電話番号', width: 200 }, { dataIndex: 'age', text: '年齢', width: 150, align: 'right', summaryCell: 'numbercell' } ] } });
pinnedFooterコンフィグを使う場合は、pinFooters: trueも併せて指定します。pinnedFooterを使うと、フッター部分をピン留めするようにグリッドのスクロール時にサマリー行が下部に留まってくれるようです。
あと、試しにサマリー行の背景色を変えようとして気づいたことがあります。
scssファイルの配置場所がjsファイルと同じところになってるようです。Main.jsと同じディレクトリにMain.scssを作成したら確かに反映されました。ExtJS7でもこれでいくのかな。
レーティング
Ext.ux.rating.Pickerで5段階のレーティングを表現できます。標準装備に加わりました。
他にもあるようですが、グリッドは以上で。次はリストを見てみるつもりです。
(ExtJS6.5)グリッドの新機能[modern] (2)
今回もExtJS6.5のmodernグリッド改善点です。
グリッドの各所にアイコンボタンを設置できるようになった!
スマフォやタブレットでお馴染みのアイコンのボタンを、グリッドの色々な箇所に設置できるようになりました。
Ext.define('Sample.view.main.Main', { extend: 'Ext.Panel', xtype: 'app-main', requires: [ 'Ext.layout.Fit' ], controller: 'main', viewModel: 'main', layout: 'fit', items: { xtype: 'grid', store: { proxy: 'memory', data: [ { id: 1, state: '鹿児島県', name: 'yamada1', tel: '09011111111' }, { id: 2, state: '鹿児島県', name: 'yamada2', tel: '09022222222' }, { id: 3, state: '鹿児島県', name: 'yamada3', tel: '09033333333' }, { id: 4, state: '鹿児島県', name: 'yamada4', tel: '09044444444' }, { id: 5, state: '鹿児島県', name: 'yamada5', tel: '09055555555' }, { id: 6, state: '鹿児島県', name: 'yamada6', tel: '09066666666' }, { id: 7, state: '鹿児島県', name: 'yamada7', tel: '09077777777' }, { id: 8, state: '鹿児島県', name: 'yamada8', tel: '09088888888' }, { id: 9, state: '鹿児島県', name: 'yamada9', tel: '09099999999' } ], grouper: { property: 'state' } }, platformConfig: { desktop: { plugins: { gridcellediting: true } }, '!desktop': { plugins: { grideditable: true } } }, grouped: true, groupHeader: { tpl: 'Group: {name}', tools: { print: { tooltip: 'Print group', zone: 'tail' }, save: { }, refresh: { } } }, columns: [ { dataIndex: 'id', text: 'ID', align: 'right' }, { dataIndex: 'name', text: '氏名', minWidth: 150, flex: 1, editable: true, tools: { gear: { zone: 'end', handler: function (column, tool, e) { // columnは列への参照。 // toolはアイコンボタンへの参照(Ext.Toolクラスのインスタンス) // eはイベントオブジェクト。 } } }, cell: { tools: { gear: { zone: 'end', handler: function (grid, config) { // gridはグリッドへの参照。 // configには、cell・column・event・grid・record・toolのプロパティが存在 } } } } }, { dataIndex: 'tel', text: '電話番号', width: 200, editor: 'textfield' } ] } });
上記のように、toolsプロパティを指定すると、これまで設置が大変だったカラムやグループの部分にもボタンを設置できます。
gearやprintのプロパティ名がアイコンの種類になるようです。Developer Toolで直接参照したところ、該当アイコンのclass属性は「x-icon-el x-font-icon x-tool-type-print」となっていたので(printの場合)、おそらくMaterial Iconsに存在するアイコンが指定できるのだと思われます。ext/modern/theme-neptune/sass/src/Tool.scssにアイコンの定義があったので、そこに存在する分は指定できそうです。無ければ同じように追加できる気がします。
あと、ツリー形式のグリッドでも対応してるみたいです(本投稿では省略します)。
カラムメニューが使える!
classicのようなカラムのメニューが実装されました。
ソート順やカラムの表示・非表示を切り替える、classicでは定番のUIですね。
さらに、メニューの項目も簡単に追加カスタマイズできるようです。
columns: [ { dataIndex: 'id', text: 'ID', align: 'right', menu: { custom: { text: 'Custom Item', separator: true, handler: 'onCustom' } } }, ・・・
(ExtJS6.5)グリッドの新機能[modern] (1)
ExtJS6.5でmodernのグリッドにいくつか変更箇所があります。
今回は選択と編集を試してみました。
選択
Ext.grid.Gridのselectableコンフィグを使って、どういう選択を可能にするかを設定できるようになりました。
items: [{ xtype: 'grid', selectable: { checkbox: true, cells: true } }]
プラグインで指定していた以前より、可読性が良くなりました。
ドキュメントを見る限り8つの項目を指定できるようです。
- mode `'single'`, `'multi'` Allow selection of only a single or multiple *records*. This is only valid when selecting rows. - deselectable Configure as false to disallow deselecting down to zero selected *records*. This is only valid when selecting rows. - drag `true` or `false` to allow drag gestures to swipt a rage of cells or rows. - columns `true` to enable column selection by clicking on headers. Defaults to `false` - cells `true` to enable cell selection by clicking or dragging on cells. Defaults to `false` - rows Set to `false` to disable selecting rows. Defaults to `true` - checkbox `true` to add a checkbox column to display selected state. `'only'` to indicate that only clicks on the checkbox affect row selected state. - extensible `true` to enable the selection to be extended either in the `X` or `Y` axis or `'x'` or `'y'` to configure
グリッドの場合は、Ext.grid.selection.Model(http://docs.sencha.com/extjs/6.5.0/modern/Ext.grid.selection.Model.html)というセレクションモデルのクラスを使っているようで、このクラスのコンフィグとして渡してくれるみたいです。
編集
Ext.grid.plugin.CellEditingプラグインクラスを使って、セル単位で編集できるようになりました。classicでは当たり前にできていましたが、modernでは新規です。
これまでの編集は、行を選択すると、編集フォームが画面外からスライドイン表示され、そこで編集作業を行うという流れでした。
しかし、PCで扱う場合は直接編集できたほうが操作感が良いため、PC向けに作成されたようです。
このことから、最終的にはPC・モバイルともにmodernにまとめたいという意思を感じます。
PC向けの場合は新しく追加されたプラグインを、モバイル向けの場合はこれまでのUIを使いたい場合は、platformConfigで設定するコンフィグを切り替えできるようです。
items: [{ xtype: 'grid', platformConfig: { desktop: { plugins: { gridcellediting: true } }, '!desktop': { plugins: { grideditable: true } } }, ...] }]
試してみると、PCのユーザエージェントだと↓のようにセル単位でのインライン編集となり
タブレットのユーザエージェントだと↓のように編集フォームでの編集操作となりました。
インライン編集は、編集中にグリッドをスクロールしても編集の行はその位置に固定された状態になるようです。なるほど、スーパーパワー!(意味が分からない人はドキュメントをお読みください)
アプリケーションで使用するExtJSのバージョンを上げる
ExtJS6.5がリリースされましたね。
https://www.sencha.com/blog/announcing-ext-js-6-5-and-sencha-cmd-6-5-ga/
いろいろ変更ありますが、modern向け機能の強化がうれしいです。
早速SDKとSenchaコマンドをダウンロードしたので、アプリケーションで使用するExtJSのバージョンを6.5に上げてみます。
> cd (アプリケーションのパス) > sencha app upgrade (ExtJS6.5 SDKのパス) Sencha Cmd v6.5.0.180 [INF] Upgrading framework ext [INF] Copying framework to (アプリケーションのパス)/ext [INF] Framework 'ext' upgraded
workspace.jsonを見ると↓のように6.5になりました。
"frameworks": { "ext": { "path":"ext", "version":"6.5.0.775" } }
更新後は.senchaディレクトリのファイルがごそっと削除されています。cfgやxmlファイルをカスタマイズしてた場合は影響を受けそうですね。6.5以降は、.senchaディレクトリを使わなくなるのかな?
app.jsonのrequiresで、modernに存在しないext-localeを定義していたらsencha app buildでエラーとなるようになっていました。
requiresはclassicとmodernで分けておいたほうが良さそうです。
あとはES6サポートを試してみました。class Panel {} みたいに書けるのかと思っていましたが、まだそこまでの対応ではないようです。
PromiseのようなES6の機能を使って実装した場合、ビルドしたときにサポートしていないブラウザでも動作するように出力してくれるだけみたいです。
おそらくExtJS7で構文が変わるんでしょうね。
今後、他の機能をちょこちょこ試していこうと思います。
あ、以降の投稿ではExtJS6.5を使うつもりです。
ダッシュボード[classic]
Ext.dashboard.Dashboardを使うと、業務系の画面でよくあるダッシュボードのUIを実現できます。
partsコンフィグに、適当なキー名でExt.dashboard.Partクラスのコンフィグを指定します。
さらにExt.dashboard.PartのviewTemplateコンフィグのitemsに表示したいビューを指定することになります。
まだpartsコンフィグだけでは表示されないです。
defaultContentコンフィグには、最低限typeとcolumnIndexを指定したオブジェクトリテラルの配列を指定します。
ここでtypeはpartsコンフィグで指定したキー名、columnIndexは列のインデックス番号(0から)です。
/** * ダッシュボードパネルクラス。 * * @class Sample.view.main.dashboard.Panel * @extend Ext.dashboard.Dashboard */ Ext.define('Sample.view.main.dashboard.Panel', { extend: 'Ext.dashboard.Dashboard', xtype: 'main_dashboard_panel', parts: { testKey: { viewTemplate: { items: { xtype: 'panel', html: 'パネル' } } } }, defaultContent: [ { type: 'testKey', columnIndex: 0 }, { type: 'testKey', columnIndex: 1 }, { type: 'testKey', columnIndex: 2 }, { type: 'testKey', columnIndex: 1 } ] });
上記だと、testKeyのビューを4つダッシュボードに表示しています。columnIndex=1を2つ指定しているので、2列目のビューは2つになっています。
ドラッグ&ドロップ操作で場所を移動させることができます。
動的にビューを追加する
ボタンクリック時にビューを追加してみます。
/** * ダッシュボードパネルクラス。 * * @class Sample.view.main.dashboard.Panel * @extend Ext.dashboard.Dashboard */ Ext.define('Sample.view.main.dashboard.Panel', { extend: 'Ext.dashboard.Dashboard', xtype: 'main_dashboard_panel', parts: { testKey: { viewTemplate: { items: { xtype: 'panel', html: 'パネル' } } } } }); /** * メインパネル。 * * @class Sample.view.main.Panel * @extend Ext.panel.Panel */ Ext.define('Sample.view.main.Panel', { extend: 'Ext.panel.Panel', xtype: 'main_panel', requires: [ 'Sample.view.main.dashboard.Panel', 'Sample.view.main.ViewController' ], controller: 'main', cls: 'main-panel', layout: 'fit', dockedItems: { xtype: 'toolbar', items: [ '->', { text: '追加', handler: 'onClickAddButton' } ] }, items: { reference: 'dashboard_panel', xtype: 'main_dashboard_panel' } }); /** * ビューコントローラクラス。 * * @class Sample.view.main.ViewController * @extend Ext.app.ViewController */ Ext.define('Sample.view.main.ViewController', { extend: 'Ext.app.ViewController', alias: 'controller.main', onClickAddButton: function () { var me = this, dashboardPanel = me.lookupReference('dashboard_panel'); dashboardPanel.addNew('testKey', 0); } });
Ext.dashboard.DashboardのaddNewメソッドで追加できます。第1引数はpartsコンフィグに定義したキー名、第2引数は列のインデックス番号です。
でも、これだとpartsコンフィグに定義されていない呼び出せないです。なので、partsコンフィグにも動的に追加してみます。
/** * ダッシュボードパネルクラス。 * * @class Sample.view.main.dashboard.Panel * @extend Ext.dashboard.Dashboard */ Ext.define('Sample.view.main.dashboard.Panel', { extend: 'Ext.dashboard.Dashboard', xtype: 'main_dashboard_panel', columnWidths: [ 0.5, 0.5 ] }); /** * メインパネル。 * * @class Sample.view.main.Panel * @extend Ext.panel.Panel */ Ext.define('Sample.view.main.Panel', { extend: 'Ext.panel.Panel', xtype: 'main_panel', requires: [ 'Sample.view.main.dashboard.Panel', 'Sample.view.main.ViewController' ], controller: 'main', cls: 'main-panel', layout: 'fit', dockedItems: { xtype: 'toolbar', items: [ '->', { text: '列1に追加', handler: 'onClickAddColumn1Button' }, { text: '列2に追加', handler: 'onClickAddColumn2Button' } ] }, items: { reference: 'dashboard_panel', xtype: 'main_dashboard_panel' } }); /** * ビューコントローラクラス。 * * @class Sample.view.main.ViewController * @extend Ext.app.ViewController */ Ext.define('Sample.view.main.ViewController', { extend: 'Ext.app.ViewController', alias: 'controller.main', /** * 列1に追加ボタンクリック時の処理。 */ onClickAddColumn1Button: function () { var me = this; // 第1,2引数は、応用を考えて仮の値を固定値で渡している。 // 実際には、他の入力フィールドの値やサーバからのレスポンスデータを渡すことが考えられる。 me.addComponent(me.getContentId(), 'panel', 0); }, /** * 列2に追加ボタンクリック時の処理。 */ onClickAddColumn2Button: function () { var me = this; me.addComponent(me.getContentId(), 'panel', 1); }, /** * コンポーネントを追加する。 * * @param {Number} id ID * @param {String} type ビューの種類 * @param {Number} columnIndex 列インデックス番号 */ addComponent: function (id, type, columnIndex) { var me = this, dashboardPanel = me.lookupReference('dashboard_panel'), partKey = me.getPartKey(id, type), parts = dashboardPanel.getParts(); dashboardPanel.setParts(me.getPartConfig(id, type), parts); dashboardPanel.addNew(partKey, columnIndex); }, /** * Ext.dashboard.Partのコンフィグを返す。 * * @param {Number} id ID * @param {String} type ビューの種類 * @returns {Object} コンフィグ */ getPartConfig: function (id, type) { var me = this, partKey = me.getPartKey(id, type), config = {}; config[partKey] = { viewTemplate: { items: me.getPartItemConfig(id, type) } }; return config; }, /** * Ext.dashboard.Partに設定するアイテムコンポーネントのコンフィグを返す。 * * @param {Number} id ID * @param {String} type ビューの種類 * @returns {Object} コンフィグ */ getPartItemConfig: function (id, type) { // MEMO: 実案件などに使う場合は、おそらくtypeに応じて返すコンフィグを切り替えたりすることになる return { xtype: type, html: 'content ' + id }; }, /** * partsコンフィグのキーを返す。 * * @param {Number} id ID * @param {String} type ビューの種類 * @returns {String} キー */ getPartKey: function (id, type) { return type + id; }, /** * IDを生成する。 * @returns {number} ID */ getContentId: function () { return Math.floor(Math.random() * 100); } });
少し応用などを考えた実装になっていますが、やりたかったことの実装部分はaddComponentメソッドです。
注意としては、Ext.dashboard.DashboardクラスのcolumnWidthsコンフィグを指定しておくことです。
columnWidthsコンフィグは列の幅の割合を、1を分け合う値で指定するのですが、これを指定していないとおかしな挙動になります。
ビューの削除やドロップのイベントについても取り上げようと思いましたが、Ext.dashboardパッケージのクラスのソースを見ればわかりそうなので今回は割愛です。