ダッシュボード[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パッケージのクラスのソースを見ればわかりそうなので今回は割愛です。
本番用のapp.jsでrequireの設定不足によるJSエラーが発生する場合の対処
ExtJSで良くあるのが、requireの記述漏れです。
本番用にsencha app buildで出力したファイルで動かしたら、↓のようなJSエラーが発生。この場合は、まずrequiresにクラス指定が漏れています。
Uncaught TypeError: c is not a constructor at eval (eval at getInstantiator (app.js:1), <anonymous>:3:8) at Object.create (app.js:1) at Ext.Inventory.instantiateByAlias (app.js:1) at ai.create (app.js:1) at ai.constructPlugin (app.js:1) at ai.constructPlugins (app.js:1) at ai.initComponent (app.js:1) at ai.initComponent (app.js:1) at ai.initComponent (app.js:1) at ai.initComponent (app.js:1)
ブラウザコンソール上に警告が出ていないか確認する
sencha app watchで動かしている場合、下記のような警告を出してくれることがあるので、この場合は速やかにrequiresに追記しましょう。
[W] [Ext.Loader] Synchronously loading 'Ext.chart.series.Line'; consider adding Ext.require('Ext.chart.series.Line') above Ext.onReady
Ext.ClassManagerのinstantiateByAliasメソッドで確認する
しかし、時々、警告が出ないパターンがあります。
その場合は、Ext.ClassManagerのinstantiateByAliasメソッド上にどうにかコンソールログなどを加えて直接見るしかありません。
画面表示時にいきなりエラーが出ないのであれば、エラー表示前に↓をブラウザコンソールで実行して、コンソールログ出力を加えます。
var originFunc = Ext.ClassManager.instantiateByAlias; Ext.ClassManager.instantiateByAlias = function () { console.log(arguments); return originFunc.apply(this, arguments); }
この状態でrequire不足のJSエラーを発生させれば、どのクラスが足りていないのかログ出力されます。
ブラウザでの初期画面表示時にエラーが出る場合は、ブラウザコンソールで実行しても間に合わないので、Application.jsのlaunchメソッドなどに上記コードを追加してしまうか直接ExtJSの該当コードにコンソールログを埋め込むなどの工夫が必要でしょう。Ext.ClassManager自体がシングルトンなので、overridesではオーバーライドはできません。
チャート[classic] (3)
スタック形式の棒グラフ
Ext.chart.series.Barのstackedコンフィグをtrueにすると、積み上げた形状の棒グラフを表現できます。
これを使うとデータを比較しやすくなります。
モデル、ストア
車の販売台数のデータでモデル、ストアを作成しました。(データ元: http://www.jada.or.jp/contents/data/hanbai/maker.html)
/** * 車販売台数モデルクラス。 * * @class Sample.model.CarSales * @extend Ext.data.Model */ Ext.define('Sample.model.CarSales', { extend: 'Ext.data.Model', fields: [ { name: 'ym', type: 'string' }, { name: 'data1', type: 'int' }, { name: 'data2', type: 'int' }, { name: 'data3', type: 'int' }, { name: 'data4', type: 'int' }, { name: 'data5', type: 'int' }, { name: 'data6', type: 'int' }, { name: 'data7', type: 'int' }, { name: 'data8', type: 'int' }, { name: 'data9', type: 'int' }, { name: 'data10', type: 'int' }, { name: 'data11', type: 'int' }, { name: 'data12', type: 'int' }, { name: 'data13', type: 'int' } ] }); /** * 車販売台数ストアクラス。 * * @class Sample.store.CarSales * @extend Ext.data.Store */ Ext.define('Sample.store.CarSales', { extend: 'Ext.data.Store', model: [ 'Sample.model.CarSales' ], model: 'Sample.model.CarSales', proxy: 'memory', data: [ { ym: '2016/04', data1: 721, data2: 7885, data3: 3681, data4: 23993, data5: 4184, data6: 9441, data7: 1879, data8: 2620, data9: 19177, data8: 2620, data9: 19177, data10: 7871, data11: 111693, data12: 670, data13: 18898 }, { ym: '2016/05', data1: 430, data2: 8243, data3: 4146, data4: 27949, data5: 4967, data6: 10484, data7: 1510, data8: 3082, data9: 24690, data8: 3082, data9: 24690, data10: 7353, data11: 105528, data12: 647, data13: 24724 }, { ym: '2016/06', data1: 491, data2: 8870, data3: 5754, data4: 33944, data5: 6549, data6: 10478, data7: 1876, data8: 4897, data9: 30561, data8: 4897, data9: 30561, data10: 9239, data11: 138878, data12: 1034, data13: 35199 }, { ym: '2016/07', data1: 676, data2: 8906, data3: 4622, data4: 31698, data5: 6716, data6: 13431, data7: 2279, data8: 3313, data9: 27747, data8: 3313, data9: 27747, data10: 8647, data11: 146536, data12: 844, data13: 26338 }, { ym: '2016/08', data1: 854, data2: 8367, data3: 4623, data4: 22381, data5: 6481, data6: 12195, data7: 1295, data8: 3316, data9: 22466, data8: 3316, data9: 22466, data10: 6438, data11: 110527, data12: 799, data13: 23531 }, { ym: '2016/09', data1: 657, data2: 11145, data3: 7168, data4: 38064, data5: 10833, data6: 18112, data7: 1505, data8: 4936, data9: 31573, data8: 4936, data9: 31573, data10: 8781, data11: 144012, data12: 1071, data13: 39191 }, { ym: '2016/10', data1: 538, data2: 9249, data3: 4527, data4: 32629, data5: 5382, data6: 10227, data7: 2279, data8: 2913, data9: 25695, data8: 2913, data9: 25695, data10: 6457, data11: 118712, data12: 791, data13: 23470 }, { ym: '2016/11', data1: 994, data2: 11832, data3: 5500, data4: 31681, data5: 6902, data6: 13537, data7: 2242, data8: 3426, data9: 34997, data8: 3426, data9: 34997, data10: 6892, data11: 126837, data12: 917, data13: 27285 }, { ym: '2016/12', data1: 1106, data2: 10269, data3: 5688, data4: 28676, data5: 7501, data6: 8564, data7: 1954, data8: 4625, data9: 31751, data8: 4625, data9: 31751, data10: 5919, data11: 124348, data12: 1045, data13: 33492 }, { ym: '2017/01', data1: 1123, data2: 12722, data3: 4380, data4: 30338, data5: 5103, data6: 13946, data7: 2301, data8: 2352, data9: 40324, data8: 2352, data9: 40324, data10: 8828, data11: 114916, data12: 577, data13: 21175 }, { ym: '2017/02', data1: 975, data2: 13218, data3: 5284, data4: 34267, data5: 6824, data6: 16027, data7: 2596, data8: 3278, data9: 44952, data8: 3278, data9: 44952, data10: 9183, data11: 147714, data12: 699, data13: 27018 }, { ym: '2017/03', data1: 1819, data2: 18496, data3: 11443, data4: 49847, data5: 13160, data6: 28751, data7: 5494, data8: 5878, data9: 64395, data8: 5878, data9: 64395, data10: 12834, data11: 200587, data12: 1366, data13: 46584 } ] });
チャート
あとはExt.chart.series.Barを使って棒グラフを表示してみます。
積み上げる順番にyFieldに複数のフィールド名を指定します。
あとはstacked: trueを指定します。
/** * チャートクラス。 * * @class Sample.views.main.chart.Panel * @extend Ext.chart.CartesianChart */ Ext.define('Sample.views.main.chart.Panel', { extend: 'Ext.chart.CartesianChart', xtype: 'chart_panel', requires: [ 'Ext.chart.axis.Numeric', 'Ext.chart.axis.Category', 'Ext.chart.series.Bar' ], store: 'CarSales', legend: { type: 'sprite', docked: 'bottom' }, axes: [ { type: 'numeric', position: 'left', minimum: 0, title: { text: '販売台数' } }, { type: 'category', position: 'bottom', label: { rotate: { degrees: -90 } }, title: { text: '年月' } } ], series: [ { type: 'bar', xField: 'ym', yField: ['data1','data2','data3','data4','data5','data6','data7','data8','data9','data10','data11','data12','data13'], title: ['ダイハツ','富士重工','日野','ホンダ','いすゞ','マツダ','三菱','三菱ふそう','日産','スズキ','トヨタ','UDトラックス','輸入車'], stacked: true, style: { minGapWidth: 20 } } ] });
legendコンフィグで、Excelの凡例みたいなのを表示できます。複数のデータがあると、どの色が何を表しているのか分からないので、この形式だと必須ですね。
スタック形式の割合で表示
さらに自動的に各データの全体での割合のチャートにすることができます。
fullStack: trueを指定するだけです。
/** * チャートクラス。 * * @class Sample.views.main.chart.Panel * @extend Ext.chart.CartesianChart */ Ext.define('Sample.views.main.chart.Panel', { extend: 'Ext.chart.CartesianChart', xtype: 'chart_panel', requires: [ 'Ext.chart.axis.Numeric', 'Ext.chart.axis.Category', 'Ext.chart.series.Bar' ], store: 'CarSales', legend: { type: 'sprite', docked: 'bottom' }, axes: [ { type: 'numeric', position: 'left', minimum: 0, title: { text: '販売台数の割合(%)' } }, { type: 'category', position: 'bottom', label: { rotate: { degrees: -90 } }, title: { text: '年月' } } ], series: [ { type: 'bar', xField: 'ym', yField: ['data1','data2','data3','data4','data5','data6','data7','data8','data9','data10','data11','data12','data13'], title: ['ダイハツ','富士重工','日野','ホンダ','いすゞ','マツダ','三菱','三菱ふそう','日産','スズキ','トヨタ','UDトラックス','輸入車'], stacked: true, fullStack: true, style: { minGapWidth: 20 } } ] });
各年月で、全体を100%としたときの割合を表示しています。
データを横に並べる
データを横並びにして比べるなら、stack: falseを指定します(stackコンフィグの初期値はtrueなんですね。知らなかった)。
/** * チャートクラス。 * * @class Sample.views.main.chart.Panel * @extend Ext.chart.CartesianChart */ Ext.define('Sample.views.main.chart.Panel', { extend: 'Ext.chart.CartesianChart', xtype: 'chart_panel', requires: [ 'Ext.chart.axis.Numeric', 'Ext.chart.axis.Category', 'Ext.chart.series.Bar' ], store: 'CarSales', legend: { type: 'sprite', docked: 'bottom' }, axes: [ { type: 'numeric', position: 'left', minimum: 0, title: { text: '販売台数' }, grid: { odd: { fillStyle: 'rgba(255, 255, 255, 0.06)' }, even: { fillStyle: 'rgba(0, 0, 0, 0.03)' } } }, { type: 'category', position: 'bottom', label: { rotate: { degrees: -90 } }, title: { text: '年月' } } ], series: [ { type: 'bar', xField: 'ym', yField: ['data11', 'data4', 'data1'], stacked: false, style: { inGroupGapWidth: -7 }, title: ['トヨタ', 'ホンダ', 'ダイハツ'] } ] });
立体的な棒グラフ
軸、データ系列のクラス名が「~3D」となっているものを使用します。その他の設定は、これまでとほぼ同じです。
/** * チャートクラス。 * * @class Sample.views.main.chart.Panel * @extend Ext.chart.CartesianChart */ Ext.define('Sample.views.main.chart.Panel', { extend: 'Ext.chart.CartesianChart', xtype: 'chart_panel', requires: [ 'Ext.chart.axis.Numeric3D', 'Ext.chart.axis.Category3D', 'Ext.chart.series.Bar3D', 'Ext.chart.grid.HorizontalGrid3D' ], store: 'CarSales', legend: { type: 'sprite', docked: 'bottom' }, axes: [ { type: 'numeric3d', position: 'left', minimum: 0, title: { text: '販売台数' }, grid: { odd: { fillStyle: 'rgba(255, 255, 255, 0.06)' }, even: { fillStyle: 'rgba(0, 0, 0, 0.03)' } } }, { type: 'category3d', position: 'bottom', label: { rotate: { degrees: -90 } }, title: { text: '年月' } } ], series: [ { type: 'bar3d', xField: 'ym', yField: ['data11', 'data4', 'data1'], stacked: false, style: { inGroupGapWidth: -7 }, title: ['トヨタ', 'ホンダ', 'ダイハツ'] } ] });
チャート[classic] (2)
今回はclassicのチャートの続きです。
いくつかチャートの種類を試してみます。
Ext.chart.series.Line
折れ線グラフです。
insetPaddingやinnerPaddingでチャートのパディングを調整しています。何らかの方法で表示を調整しないと、テキストがチャート外にはみ出すことがあるので注意が必要です。
/** * チャートクラス。 * * @class Sample.views.main.chart.Panel * @extend Ext.chart.CartesianChart */ Ext.define('Sample.views.main.chart.Panel', { extend: 'Ext.chart.CartesianChart', xtype: 'chart_panel', requires: [ 'Ext.chart.axis.Numeric', 'Ext.chart.axis.Category', 'Ext.chart.interactions.ItemHighlight', 'Ext.chart.series.Line' ], store: 'WaterStorage', insetPadding: { top: 40, bottom: 40, left: 20, right: 40 }, innerPadding: { top: 20, left: 40, right: 40 }, axes: [ { type: 'numeric', position: 'left', minimum: 0, title: { text: '貯水量' } }, { type: 'category', position: 'bottom', title: { text: '年月' } } ], series: [ { type: 'line', xField: 'ym', yField: 'amount', style: { stroke: '#ad5987', lineWidth: 2 }, marker: { type: 'circle', radius: 4, lineWidth: 2, fill: '#ad5987' }, label: { field: 'amount', display: 'under' } } ] });
Ext.chart.series.Bar
棒グラフです。
/** * チャートクラス。 * * @class Sample.views.main.chart.Panel * @extend Ext.chart.CartesianChart */ Ext.define('Sample.views.main.chart.Panel', { extend: 'Ext.chart.CartesianChart', xtype: 'chart_panel', store: 'WaterStorage', axes: [ { type: 'numeric', position: 'left', minimum: 0, title: { text: '貯水量' } }, { type: 'category', position: 'bottom', title: { text: '年月' } } ], series: [ { type: 'bar', xField: 'ym', yField: 'amount', style: { minGapWidth: 20, stroke: '#50ada1', fill: '#50ada1' }, highlight: { strokeStyle: '#4e7d9c', fillStyle: '#4e7d9c' }, label: { field: 'amount', display: 'insideEnd' } } ] });
↓のように水平方向にもできます。
Ext.chart.CartesianChartのflipXYコンフィグをtrueで指定し、軸(axes)の位置(position)を入れ替えています。
一応理屈は同じなので、Ext.chart.series.Lineなどでも可能です(需要はなさそうですが)。
/** * チャートクラス。 * * @class Sample.views.main.chart.Panel * @extend Ext.chart.CartesianChart */ Ext.define('Sample.views.main.chart.Panel', { extend: 'Ext.chart.CartesianChart', xtype: 'chart_panel', requires: [ 'Ext.chart.axis.Numeric', 'Ext.chart.axis.Category', 'Ext.chart.interactions.ItemHighlight', 'Ext.chart.series.Bar' ], store: 'WaterStorage', flipXY: true, axes: [ { type: 'numeric', position: 'bottom', minimum: 0, title: { text: '貯水量' } }, { type: 'category', position: 'left', title: { text: '年月' } } ], series: [ { type: 'bar', xField: 'ym', yField: 'amount', style: { stroke: '#50ada1', fill: '#50ada1' }, highlight: { strokeStyle: '#4e7d9c', fillStyle: '#4e7d9c' }, label: { field: 'amount', display: 'insideEnd' } } ] });
Ext.chart.series.Area
面グラフです。
highlightCfgコンフィグで、マウスがマーカーに近づいたときにマーカーのスタイルを変更できます。
あとtooltipコンフィグで、マウスがマーカーに近づいたときにツールチップを表示させています。
/** * チャートクラス。 * * @class Sample.views.main.chart.Panel * @extend Ext.chart.CartesianChart */ Ext.define('Sample.views.main.chart.Panel', { extend: 'Ext.chart.CartesianChart', xtype: 'chart_panel', requires: [ 'Ext.chart.axis.Numeric', 'Ext.chart.axis.Category', 'Ext.chart.interactions.ItemHighlight', 'Ext.chart.series.Area' ], store: 'WaterStorage', axes: [ { type: 'numeric', position: 'left', grid: true, minimum: 0, title: { text: '貯水量' } }, { type: 'category', position: 'bottom', label: { rotate: { degrees: -90 } }, title: { text: '年月' } } ], series: [ { type: 'area', xField: 'ym', yField: 'amount', style: { stroke: '#ad5987', fill: '#d9aac4', opacity: 0.6, lineWidth: 1 }, marker: { opacity: 0, scaling: 0, fx: { duration: 200, easing: 'easeOut' } }, highlightCfg: { opacity: 1, scaling: 1.5 }, tooltip: { trackMouse: true, renderer: function (tooltip, record, item) { tooltip.setHtml(record.get('ym') + ' : ' + record.get('amount')); } } } ] });
とりあえず代表的なグラフを取り上げました。
まだいくつも種類があるので、もう少しチャート回が続きます。
チャート[classic] (1)
今回はclassicのチャートです。
まずはチャートを使うための基本を押さえておきます。
app.json
チャートの機能は別パッケージになっているので、app.jsonのrequiresにチャートのパッケージ名chartsを指定します。
"requires": [ "charts" ]
実装イメージ
Ext.chartパッケージに~Chartというクラスがいくつか存在します。
チャートを表示するときは、軸(axesコンフィグ)とデータ系列(seriesコンフィグ)をそれらクラスに指定します。
この辺りは、数学で最初のころにグラフを覚えたときと同じです。
まずx, y軸の線を引いて、それからデータをプロットし、線グラフにしたり棒グラフにしたりといった感じでグラフを書いていくことでしょう。
ExtJSのチャートでも、axesで軸を定義し、seriesコンフィグで線グラフや棒グラフを指定します。
実装
チャートのデータはやはりストアで管理します。
なので、まずはモデルとストアを用意しました。
モデル、ストア
/** * 貯水量モデルクラス。 * * @class Sample.model.WaterStorage * @extend Ext.data.Model */ Ext.define('Sample.model.WaterStorage', { extend: 'Ext.data.Model', fields: [ { // 年月 name: 'ym', type: 'string' }, { // 量 name: 'amount', type: 'int' } ] }); /** * 貯水量ストアクラス。 * * @class Sample.store.WaterStorage * @extend Ext.data.Store */ Ext.define('Sample.store.WaterStorage', { extend: 'Ext.data.Store', requires: [ 'Sample.model.WaterStorage' ], model: 'Sample.model.WaterStorage', proxy: 'memory', data: [ { ym: '2016/01', amount: 100 }, { ym: '2016/02', amount: 50 }, { ym: '2016/03', amount: 30 }, { ym: '2016/04', amount: 70 }, { ym: '2016/05', amount: 50 }, { ym: '2016/06', amount: 40 }, { ym: '2016/07', amount: 30 }, { ym: '2016/08', amount: 20 }, { ym: '2016/09', amount: 40 }, { ym: '2016/10', amount: 50 }, { ym: '2016/11', amount: 60 }, { ym: '2016/12', amount: 50 } ] });
チャートクラスを作成
Ext.chart.CartesianChartを継承したチャートクラスを作成します。
最低限、store, axes, seriesコンフィグの指定が必要です。
/** * チャートクラス。 * * @class Sample.views.main.chart.Panel * @extend Ext.chart.CartesianChart */ Ext.define('Sample.views.main.chart.Panel', { extend: 'Ext.chart.CartesianChart', xtype: 'chart_panel', requires: [ 'Ext.chart.axis.Numeric', 'Ext.chart.axis.Category', 'Ext.chart.interactions.ItemHighlight', 'Ext.chart.series.Line' ], store: 'WaterStorage', axes: [ { type: 'numeric', position: 'left', minimum: 0, title: { text: '貯水量' } }, { type: 'category', position: 'bottom', title: { text: '年月' } } ], series: [ { type: 'line', xField: 'ym', yField: 'amount', style: { stroke: '#ad5987', lineWidth: 2 }, marker: { type: 'circle', radius: 4, lineWidth: 2, fill: '#ad5987' }, label: { field: 'amount', display: 'over' } } ] });
axesコンフィグには、x軸とy軸の情報を指定します。y軸は貯水量を表現したいので、数値を扱えるtype: 'numeric'、x軸は各年月を表現したいので、どの型でもそのまま分類として扱えるtype: 'category'です。あと、どの位置かをpositionで設定します。
seriesコンフィグには、type: 'line'を設定していますが、これは線グラフのデータ系列です。x軸、y軸のデータとしてモデルのどのフィールドを使うかxField, yFieldで指定します。他のstyleやmarkerなどでスタイルなどの補足的な指定ができます。あと、チャート内に表示されている数値は、labelコンフィグが指定されているためです。
これでチャートを使うための基本は押さえました。
次回はExtJSで提供されている色々なチャートを使ってみます。
レイアウト[modern]
今回はmodernのレイアウトを取り上げます。
classicと説明が同じになっているところは、たぶん気のせいです。
Ext.layout.Default
これは特にレイアウトを指定していない場合に適用されているデフォルトのレイアウトです。
この場合、単純にDOMを順番に配置するだけです。アイテムコンポーネントのスタイル次第です。
/** * レイアウトDefaultのパネル。 * * @class Sample.view.main.layout.DefaultPanel * @extend Ext.Panel */ Ext.define('Sample.view.main.layout.DefaultPanel', { extend: 'Ext.Panel', xtype: 'layout_default_panel', layout: 'default', items: [ { xtype: 'component', cls: 'item-cmp1', html: 'アイテムコンポーネント1' }, { xtype: 'component', cls: 'item-cmp2', html: 'アイテムコンポーネント2' }, { xtype: 'component', cls: 'item-cmp3', html: 'アイテムコンポーネント3' } ] });
Ext.layout.Fit
アイテムコンポーネントを、layoutコンフィグを設定した親コンポーネントのサイズまで目一杯広げて設置するレイアウトです。
アイテムコンポーネントが複数ある場合は、表示できるものが1つだけ表示されます。
/** * レイアウトFitのパネル。 * * @class Sample.view.main.layout.FitPanel * @extend Ext.Panel */ Ext.define('Sample.view.main.layout.FitPanel', { extend: 'Ext.Panel', xtype: 'layout_fit_panel', layout: 'fit', items: [ { xtype: 'component', cls: 'item-cmp1', html: 'アイテムコンポーネント1' }, { xtype: 'component', cls: 'item-cmp2', html: 'アイテムコンポーネント2' }, { xtype: 'component', cls: 'item-cmp3', html: 'アイテムコンポーネント3' } ] });
Ext.layout.Card
アイテムコンポーネントを、layoutコンフィグを設定した親コンポーネントのサイズまで目一杯広げて設置するレイアウトです。
アイテムコンポーネントが複数ある場合は、インデックス番号がより小さい表示可能な(非表示になっていない)アイテムコンポーネントが1つだけ表示されます。
Ext.layout.Fitに似ていますが、さらに表示するアイテムコンポーネントを切り替える機能を持ちます。
/** * レイアウトCardのパネル。 * * @class Sample.view.main.layout.CardPanel * @extend Ext.Panel */ Ext.define('Sample.view.main.layout.CardPanel', { extend: 'Ext.Panel', xtype: 'layout_card_panel', layout: 'card', items: [ { xtype: 'component', cls: 'item-cmp1', html: 'アイテムコンポーネント1' }, { xtype: 'component', cls: 'item-cmp2', html: 'アイテムコンポーネント2' }, { xtype: 'component', cls: 'item-cmp3', html: 'アイテムコンポーネント3' } ] }); /** * メインパネル。 * * @class Sample.view.main.Panel * @extend Ext.Panel */ Ext.define('Sample.view.main.Panel', { extend: 'Ext.Panel', xtype: 'main_panel', cls: 'main-panel', requires: [ 'Sample.view.main.layout.CardPanel' ], layout: 'fit', tools: [ { xtype: 'button', text: 'Next', ui: 'action', handler: function (btn) { var mainPanel = btn.up('main_panel'), cardPanel = mainPanel.down('layout_card_panel'), nextActive = cardPanel.items.indexOf(cardPanel.getActiveItem()) + 1; if (nextActive >= cardPanel.items.length) { nextActive = 0; } cardPanel.setActiveItem(nextActive); } } ], items: { xtype: 'layout_card_panel' } });
Ext.layout.Float
アイテムコンポーネントをfloatで並べます。
/** * レイアウトFloatのパネル。 * * @class Sample.view.main.layout.FloatPanel * @extend Ext.Panel */ Ext.define('Sample.view.main.layout.FloatPanel', { extend: 'Ext.Panel', xtype: 'layout_float_panel', layout: 'float', items: [ { xtype: 'component', cls: 'item-cmp1', html: 'アイテムコンポーネント1' }, { xtype: 'component', cls: 'item-cmp2', html: 'アイテムコンポーネント2' }, { xtype: 'component', cls: 'item-cmp3', html: 'アイテムコンポーネント3' } ] });
Ext.layout.HBox
アイテムコンポーネントを横に並べるレイアウトです。
アイテムコンポーネントが右端に到達しても折り返しされません。
/** * レイアウトHBoxのパネル。 * * @class Sample.view.main.layout.HboxPanel * @extend Ext.Panel */ Ext.define('Sample.view.main.layout.HboxPanel', { extend: 'Ext.Panel', xtype: 'layout_hbox_panel', scrollable: true, layout: 'hbox', items: [ { xtype: 'component', cls: 'item-cmp1', html: 'アイテムコンポーネント1' }, { xtype: 'component', cls: 'item-cmp2', html: 'アイテムコンポーネント2' }, { xtype: 'component', cls: 'item-cmp3', html: 'アイテムコンポーネント3' } ] });
レイアウトのalignコンフィグで、アイテムコンポーネントの縦方向に対する設定が可能です。alignは↓のように指定します。
layout: { type: 'hbox', align: 'middle' }
middle | end | stretch |
---|---|---|
縦の位置を中央揃えにします。 | 縦の位置を下端に揃えます | 高さを目一杯広げます |
レイアウトのpackコンフィグで、アイテムコンポーネントの横方向に対する設定が可能です。
center | end |
---|---|
横の位置を中央揃えにします | 横の位置を右端に寄せます |
アイテムコンポーネントにflexを指定することで、flexを指定したアイテムコンポーネントだけ目一杯幅を広げることができます。
/** * レイアウトHBoxのパネル。 * * @class Sample.view.main.layout.HboxPanel * @extend Ext.Panel */ Ext.define('Sample.view.main.layout.HboxPanel', { extend: 'Ext.Panel', xtype: 'layout_hbox_panel', layout: 'hbox', items: [ { xtype: 'component', cls: 'item-cmp1', html: 'アイテムコンポーネント1', flex: 1 }, { xtype: 'component', cls: 'item-cmp2', html: 'アイテムコンポーネント2', flex: 2 }, { xtype: 'component', cls: 'item-cmp3', html: 'アイテムコンポーネント3', flex: 1 } ] });
flexを指定したアイテムコンポーネントがある場合、余った部分をflexの値の比率で分け合います。
Ext.layout.VBox
アイテムコンポーネントを縦に並べるレイアウトです。
向きが変わるだけで、Ext.layout.HBoxと同じようなことができます。
/** * レイアウトVBoxのパネル。 * * @class Sample.view.main.layout.VboxPanel * @extend Ext.Panel */ Ext.define('Sample.view.main.layout.VboxPanel', { extend: 'Ext.Panel', xtype: 'layout_vbox_panel', layout: 'vbox', items: [ { xtype: 'component', cls: 'item-cmp1', html: 'アイテムコンポーネント1' }, { xtype: 'component', cls: 'item-cmp2', html: 'アイテムコンポーネント2' }, { xtype: 'component', cls: 'item-cmp3', html: 'アイテムコンポーネント3' } ] });
レイアウトのalignコンフィグで、アイテムコンポーネントの横方向に対する設定が可能です。alignは↓のように指定します。
middle | end | stretch |
---|---|---|
横の位置を中央揃えにします。 | 横の位置を右端に揃えます | 幅を目一杯広げます |
レイアウトのpackコンフィグで、アイテムコンポーネントの縦方向に対する設定が可能です。
center | end |
---|---|
縦の位置を中央揃えにします | 縦の位置を下端に寄せます |
アイテムコンポーネントにflexを指定することで、flexを指定したアイテムコンポーネントだけ目一杯高さを広げることができます。
/** * レイアウトVBoxのパネル。 * * @class Sample.view.main.layout.VboxPanel * @extend Ext.Panel */ Ext.define('Sample.view.main.layout.VboxPanel', { extend: 'Ext.Panel', xtype: 'layout_vbox_panel', layout: 'vbox', items: [ { xtype: 'component', cls: 'item-cmp1', html: 'アイテムコンポーネント1', flex: 1 }, { xtype: 'component', cls: 'item-cmp2', html: 'アイテムコンポーネント2', flex: 2 }, { xtype: 'component', cls: 'item-cmp3', html: 'アイテムコンポーネント3' } ] });
レイアウト[classic] (3)
classicのレイアウトは最後です。
Ext.layout.container.Column
アイテムコンポーネントを横に並べるレイアウトです。
端まで到達すると、floatのように自動的に折り返すところがHBoxと異なります。
/** * レイアウトColumnのパネル。 * * @class Sample.view.main.layout.ColumnPanel * @extend Ext.Panel */ Ext.define('Sample.view.main.layout.ColumnPanel', { extend: 'Ext.Panel', xtype: 'layout_column_panel', layout: 'column', items: [ { xtype: 'component', cls: 'item-cmp1', html: 'アイテムコンポーネント1' }, { xtype: 'component', cls: 'item-cmp2', html: 'アイテムコンポーネント2' }, { xtype: 'component', cls: 'item-cmp3', html: 'アイテムコンポーネント3' } ] });
アイテムコンポーネントにcolumnWidthコンフィグを指定すると、幅を比率で指定することができます。
/** * レイアウトColumnのパネル。 * * @class Sample.view.main.layout.ColumnPanel * @extend Ext.Panel */ Ext.define('Sample.view.main.layout.ColumnPanel', { extend: 'Ext.Panel', xtype: 'layout_column_panel', layout: 'column', items: [ { xtype: 'component', cls: 'item-cmp1', html: 'アイテムコンポーネント1', columnWidth: 0.25 }, { xtype: 'component', cls: 'item-cmp2', html: 'アイテムコンポーネント2', columnWidth: 0.75 }, { xtype: 'component', cls: 'item-cmp3', html: 'アイテムコンポーネント3', columnWidth: 0.5 } ] });
Ext.layout.container.Table
アイテムコンポーネントを表形式にレイアウトします。
イメージとしては、HTMLのtableタグです。実際に作成されるDOMはtableになります。
/** * レイアウトTableのパネル。 * * @class Sample.view.main.layout.TablePanel * @extend Ext.Panel */ Ext.define('Sample.view.main.layout.TablePanel', { extend: 'Ext.Panel', xtype: 'layout_table_panel', layout: 'table', items: [ { xtype: 'component', cls: 'item-cmp1', html: 'アイテムコンポーネント1' }, { xtype: 'component', cls: 'item-cmp2', html: 'アイテムコンポーネント2' }, { xtype: 'component', cls: 'item-cmp3', html: 'アイテムコンポーネント3' } ] });
Ext.layout.container.Tableのcolumnsコンフィグを指定することで、表の列数を設定できます。
アイテムコンポーネントにrowspanやcolspanコンフィグを指定すると、列方向や行方向のセル結合も可能です。
/** * レイアウトTableのパネル。 * * @class Sample.view.main.layout.TablePanel * @extend Ext.Panel */ Ext.define('Sample.view.main.layout.TablePanel', { extend: 'Ext.Panel', xtype: 'layout_table_panel', layout: { type: 'table', columns: 2 }, items: [ { xtype: 'component', cls: 'item-cmp1', html: 'アイテムコンポーネント1', colspan: 2 }, { xtype: 'component', cls: 'item-cmp2', html: 'アイテムコンポーネント2' }, { xtype: 'component', cls: 'item-cmp3', html: 'アイテムコンポーネント3' } ] });
あと、アイテムコンポーネントにcellClsコンフィグを指定すると、td要素にクラス名を設定できます。
Ext.layout.container.Absolute
アイテムコンポーネントを絶対位置で配置できます。
座標はx, yコンフィグで指定します。
/** * レイアウトAbsoluteのパネル。 * * @class Sample.view.main.layout.AbsolutePanel * @extend Ext.Panel */ Ext.define('Sample.view.main.layout.AbsolutePanel', { extend: 'Ext.Panel', xtype: 'layout_absolute_panel', layout: 'absolute', items: [ { xtype: 'component', cls: 'item-cmp1', html: 'アイテムコンポーネント1', x: 100, y: 100 }, { xtype: 'component', cls: 'item-cmp2', html: 'アイテムコンポーネント2', x: 30, y: 150 }, { xtype: 'component', cls: 'item-cmp3', html: 'アイテムコンポーネント3' } ] });