読者です 読者をやめる 読者になる 読者になる

初心者のためのExtJS入門

ExtJSを使うので、ついでにまとめていきます

本番用の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メソッドなどに上記コードを追加してしまうなどの工夫が必要でしょう。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
            }
        }
    ]
});

f:id:sham-memo:20170427203121p:plain

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
            }
        }
    ]
});

f:id:sham-memo:20170427203336p:plain

各年月で、全体を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: ['トヨタ', 'ホンダ', 'ダイハツ']
        }
    ]
});

f:id:sham-memo:20170429151744p:plain

立体的な棒グラフ

軸、データ系列のクラス名が「~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: ['トヨタ', 'ホンダ', 'ダイハツ']
        }
    ]
});

f:id:sham-memo:20170429152018p:plain

チャート[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'
            }
        }
    ]
});

f:id:sham-memo:20170423133225p:plain

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'
            }
        }
    ]

});

f:id:sham-memo:20170423133239p:plain

↓のように水平方向にもできます。

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'
            }
        }
    ]
});

f:id:sham-memo:20170423140730p:plain

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'));
                }
            }
        }
    ]
});

f:id:sham-memo:20170423140741p:plain

とりあえず代表的なグラフを取り上げました。

まだいくつも種類があるので、もう少しチャート回が続きます。

チャート[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コンフィグが指定されているためです。

f:id:sham-memo:20170422161755p:plain

これでチャートを使うための基本は押さえました。

次回は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'
        }
    ]
});

f:id:sham-memo:20170416143252p:plain

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'
        }
    ]
});

f:id:sham-memo:20170416143638p:plain

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'
    }
});

f:id:sham-memo:20170416211640p:plain

f:id:sham-memo:20170416211653p:plain

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'
        }
    ]
});

f:id:sham-memo:20170416212229p:plain

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'
        }
    ]
});

f:id:sham-memo:20170416213916p:plain

レイアウトのalignコンフィグで、アイテムコンポーネントの縦方向に対する設定が可能です。alignは↓のように指定します。

layout: {
    type: 'hbox',
    align: 'middle'
}
middle end stretch
縦の位置を中央揃えにします。 縦の位置を下端に揃えます 高さを目一杯広げます
f:id:sham-memo:20170416214143p:plain f:id:sham-memo:20170416214247p:plain f:id:sham-memo:20170416213916p:plain

レイアウトのpackコンフィグで、アイテムコンポーネントの横方向に対する設定が可能です。

center end
横の位置を中央揃えにします 横の位置を右端に寄せます
f:id:sham-memo:20170416214755p:plain f:id:sham-memo:20170416214844p:plain

アイテムコンポーネント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
        }
    ]
});

f:id:sham-memo:20170416215152p:plain

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'
        }
    ]
});

f:id:sham-memo:20170416215444p:plain

レイアウトのalignコンフィグで、アイテムコンポーネントの横方向に対する設定が可能です。alignは↓のように指定します。

middle end stretch
横の位置を中央揃えにします。 横の位置を右端に揃えます 幅を目一杯広げます
f:id:sham-memo:20170416215544p:plain f:id:sham-memo:20170416215641p:plain f:id:sham-memo:20170416215444p:plain

レイアウトのpackコンフィグで、アイテムコンポーネントの縦方向に対する設定が可能です。

center end
縦の位置を中央揃えにします 縦の位置を下端に寄せます
f:id:sham-memo:20170416215801p:plain f:id:sham-memo:20170416215906p:plain

アイテムコンポーネント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'
        }
    ]
});

f:id:sham-memo:20170416220023p:plain

レイアウト[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'
        }
    ]
});

f:id:sham-memo:20170415133005p:plain

f:id:sham-memo:20170415132957p:plain

アイテムコンポーネントに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
        }
    ]
});

f:id:sham-memo:20170415133550p:plain

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'
        }
    ]
});

f:id:sham-memo:20170416135733p:plain

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'
        }
    ]
});

f:id:sham-memo:20170416140256p:plain

あと、アイテムコンポーネントに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'
        }
    ]

});

f:id:sham-memo:20170415132148p:plain

レイアウト[classic] (2)

続きです。

Ext.layout.container.Card

アイテムコンポーネントを、layoutコンフィグを設定した親コンポーネントのサイズまで目一杯広げて設置するレイアウトです。

アイテムコンポーネントが複数ある場合は、インデックス番号がより小さい表示可能な(非表示になっていない)アイテムコンポーネントが1つだけ表示されます。

Ext.layout.container.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'
        }
    ],

    dockedItems: {
        dock: 'bottom',
        xtype: 'button',
        text: 'Next',
        handler: function (btn) {
            var panel = btn.up(),
                layout = panel.getLayout(),
                nextActive = panel.items.indexOf(layout.getActiveItem()) + 1;

            if (nextActive >= panel.items.length) {
                nextActive = 0;
            }

            layout.setActiveItem(nextActive);
        }
    }
});

f:id:sham-memo:20170404002652p:plain

f:id:sham-memo:20170404002701p:plain

Nextボタンを押すと、次のアイテムコンポーネントに切り替わります。

getActiveItem、setActiveItemメソッドを使って、現在表示されているアイテムコンポーネントを取得したり切り替えたりできます。

Ext.layout.container.Border

アイテムコンポーネントを、layoutコンフィグを設定した親コンポーネントの中央・東・西・南・北の位置に配置するレイアウトです。

どの位置に配置するかは、アイテムコンポーネントのregionコンフィグで指定します。

位置 regionに指定する文字列
中央 center
east
西 west
south
north
/**
 * レイアウトBorderのパネル。
 *
 * @class Sample.view.main.layout.BorderPanel
 * @extend Ext.Panel
 */
Ext.define('Sample.view.main.layout.BorderPanel', {
    extend: 'Ext.Panel',
    xtype: 'layout_border_panel',

    layout: 'border',

    items: [
        {
            region: 'center',
            xtype: 'component',
            cls: 'item-cmp1',
            html: 'アイテムコンポーネント1'
        },
        {
            region: 'north',
            xtype: 'component',
            cls: 'item-cmp2',
            html: 'アイテムコンポーネント2'
        },
        {
            region: 'east',
            xtype: 'component',
            cls: 'item-cmp3',
            html: 'アイテムコンポーネント3'
        }
    ]
});

f:id:sham-memo:20170414235539p:plain

アイテムコンポーネントにweightコンフィグを指定すると、東西南北のどれを目一杯広げるかを制御できます。

weightの値が大きい方が優先されます。

/**
 * レイアウトBorderのパネル。
 *
 * @class Sample.view.main.layout.BorderPanel
 * @extend Ext.Panel
 */
Ext.define('Sample.view.main.layout.BorderPanel', {
    extend: 'Ext.Panel',
    xtype: 'layout_border_panel',

    layout: 'border',

    items: [
        {
            region: 'center',
            xtype: 'component',
            cls: 'item-cmp1',
            html: 'アイテムコンポーネント1'
        },
        {
            region: 'north',
            xtype: 'component',
            cls: 'item-cmp2',
            html: 'アイテムコンポーネント2',
            weight: 1
        },
        {
            region: 'east',
            xtype: 'component',
            cls: 'item-cmp3',
            html: 'アイテムコンポーネント3',
            weight: 2
        }
    ]
});

f:id:sham-memo:20170414235553p:plain

上記の場合、アイテムコンポーネント2よりアイテムコンポーネント3のほうがweightの値が大きいので、アイテムコンポーネント3が目一杯大きく表示されています。

Ext.layout.container.Anchor

このレイアウトでは、VBoxのようにアイテムコンポーネントは縦に並びます。

ただし、アイテムコンポーネントの大きさを親の大きさと相対的に指定することができるという特徴があります。

/**
 * レイアウトAnchorのパネル。
 *
 * @class Sample.view.main.layout.AnchorPanel
 * @extend Ext.Panel
 */
Ext.define('Sample.view.main.layout.AnchorPanel', {
    extend: 'Ext.Panel',
    xtype: 'layout_anchor_panel',

    scrollable: true,

    layout: 'anchor',

    items: [
        {
            xtype: 'component',
            cls: 'item-cmp1',
            html: 'アイテムコンポーネント1',
            anchor: '75% 20%'
        },
        {
            xtype: 'component',
            cls: 'item-cmp2',
            html: 'アイテムコンポーネント2',
            anchor: '-300 -200'
        },
        {
            xtype: 'component',
            cls: 'item-cmp3',
            html: 'アイテムコンポーネント3',
            anchor: '-250 20%'
        }
    ]
});

f:id:sham-memo:20170415123718p:plain

アイテムコンポーネントのanchorコンフィグには、親との相対サイズ(幅 高さ)をパーセントまたはピクセルで指定します。

上記の場合、アイテムコンポーネント1はanchor: ‘75% 20%'としていますが、これは↓を意味します。

幅 = (親コンポーネントの幅) * 0.75
高さ = (親コンポーネントの高さ) * 0.2

また、アイテムコンポーネント2のanchor: ‘-300 -200'は↓を意味します。

幅 = (親コンポーネントの幅) - 300px
高さ = (親コンポーネントの高さ) - 200px

Ext.layout.container.Accordion

今では当たり前となったアコーディオンレイアウトです。

アコーディオンレイアウトの親コンポーネントに複数のアイテムコンポーネントを設置すると、1つだけ開くことができます。

注意点としては、アイテムコンポーネントはExt.Panelでなければなりません(もしくはExt.Panelを継承したクラス)。

なので、↓でもxtype: ‘panel'としています。

/**
 * レイアウトAccordionのパネル。
 *
 * @class Sample.view.main.layout.AccordionPanel
 * @extend Ext.Panel
 */
Ext.define('Sample.view.main.layout.AccordionPanel', {
    extend: 'Ext.Panel',
    xtype: 'layout_accordion_panel',

    layout: 'accordion',

    items: [
        {
            xtype: 'panel',
            cls: 'item-panel1',
            title: 'アイテムコンポーネント1'
        },
        {
            xtype: 'panel',
            cls: 'item-panel2',
            title: 'アイテムコンポーネント2'
        },
        {
            xtype: 'panel',
            cls: 'item-panel3',
            title: 'アイテムコンポーネント3'
        }
    ]
});

f:id:sham-memo:20170415125456p:plain

f:id:sham-memo:20170415125505p:plain

Ext.layout.container.Center

アイテムコンポーネントを親コンポーネントの中央に配置するレイアウトです。

アイテムコンポーネントが複数ある場合は、最後の1つが表示されます。

/**
 * レイアウトCenterのパネル。
 *
 * @class Sample.view.main.layout.CenterPanel
 * @extend Ext.Panel
 */
Ext.define('Sample.view.main.layout.CenterPanel', {
    extend: 'Ext.Panel',
    xtype: 'layout_center_panel',

    layout: 'center',

    items: [
        {
            xtype: 'component',
            cls: 'item-cmp1',
            html: 'アイテムコンポーネント1',
            width: 200,
            height: 100
        }
    ]
});

f:id:sham-memo:20170415130209p:plain