Exemplo ExtJS 4 MVC: Complex DashBoard (Grid, Form e Gráficos)
Mais um exemplo MVC de ExtJS 4 aqui no blog. Hoje vamos ver o código do exemplo Complex Dashboard.
Vamos lá!
Estrutura do Projeto:
Model - Stock
[code lang="js" firstline="1" toolbar="true" collapse="false" wraplines="false"]
Ext.define('ExtMVC.model.Stock', {
extend: 'Ext.data.Model',
fields: [
{name: 'company'},
{name: 'price', type: 'float'},
{name: 'revenue %', type: 'float'},
{name: 'growth %', type: 'float'},
{name: 'product %', type: 'float'},
{name: 'market %', type: 'float'}
]
});
[/code]
Model - RadarDataSet
[code lang="js" firstline="1" toolbar="true" collapse="false" wraplines="false"]
Ext.define('ExtMVC.model.RadarDataSet', {
extend: 'Ext.data.Model',
fields: ['Name', 'Data']
});
[/code]
Store - Stock
[code lang="js" firstline="1" toolbar="true" collapse="false" wraplines="false"]
Ext.define('ExtMVC.store.Stocks', {
extend: 'Ext.data.ArrayStore',
model: 'ExtMVC.model.Stock'
});
[/code]
Store - RadarDataSet
[code lang="js" firstline="1" toolbar="true" collapse="false" wraplines="false"]
Ext.define('ExtMVC.store.RadarDataSets', {
extend: 'Ext.data.ArrayStore',
model: 'ExtMVC.model.RadarDataSet',
proxy: {
type: 'memory',
reader: {
type: 'json'
}
},
data: [
{
'Name': 'Price',
'Data': 100
}, {
'Name': 'Revenue %',
'Data': 100
}, {
'Name': 'Growth %',
'Data': 100
}, {
'Name': 'Product %',
'Data': 100
}, {
'Name': 'Market %',
'Data': 100
}]
});
[/code]
View - chart - StockBar
[code lang="js" firstline="1" toolbar="true" collapse="false" wraplines="false"]
Ext.define('ExtMVC.view.chart.StockBar', {
extend: 'Ext.chart.Chart',
alias : 'widget.stockbar',
flex: 1,
shadow: true,
animate: true,
store: 'Stocks',
axes: [{
type: 'Numeric',
position: 'left',
fields: ['price'],
minimum: 0,
hidden: true
}, {
type: 'Category',
position: 'bottom',
fields: ['company'],
label: {
renderer: function(v) {
return Ext.String.ellipsis(v, 15, false);
},
font: '9px Arial',
rotate: {
degrees: 270
}
}
}],
series: [{
type: 'column',
axis: 'left',
highlight: true,
style: {
fill: '#456d9f'
},
highlightCfg: {
fill: '#a2b5ca'
},
label: {
contrast: true,
display: 'insideEnd',
field: 'price',
color: '#000',
orientation: 'vertical',
'text-anchor': 'middle'
},
xField: 'name',
yField: ['price']
}]
});
[/code]
View - chart - StockRadar
[code lang="js" firstline="1" toolbar="true" collapse="false" wraplines="false"]
Ext.define('ExtMVC.view.chart.StockRadar', {
extend: 'Ext.chart.Chart',
alias : 'widget.stockradar',
margin: '0 0 0 0',
insetPadding: 20,
flex: 1.2,
animate: true,
store: 'RadarDataSets',
theme: 'Blue',
axes: [{
steps: 5,
type: 'Radial',
position: 'radial',
maximum: 100
}],
series: [{
type: 'radar',
xField: 'Name',
yField: 'Data',
showInLegend: false,
showMarkers: true,
markerConfig: {
radius: 4,
size: 4,
fill: 'rgb(69,109,159)'
},
style: {
fill: 'rgb(194,214,240)',
opacity: 0.5,
'stroke-width': 0.5
}
}]
});
[/code]
View - stocks- StockGrid
[code lang="js" firstline="1" toolbar="true" collapse="false" wraplines="false"]
Ext.define('ExtMVC.view.stocks.StockGrid', {
extend: 'Ext.grid.Panel',
alias : 'widget.stockgrid',
id: 'company-form',
flex: 0.60,
store: 'Stocks',
title:'Company Data',
perc: function(v) {
return v + '%';
},
initComponent: function() {
this.columns= [
{
id :'company',
text : 'Company',
flex: 1,
sortable : true,
dataIndex: 'company'
},
{
text : 'Price',
width : 75,
sortable : true,
dataIndex: 'price',
align: 'right',
renderer : 'usMoney'
},
{
text : 'Revenue',
width : 75,
sortable : true,
align: 'right',
dataIndex: 'revenue %',
renderer: this.perc
},
{
text : 'Growth',
width : 75,
sortable : true,
align: 'right',
dataIndex: 'growth %',
renderer: this.perc
},
{
text : 'Product',
width : 75,
sortable : true,
align: 'right',
dataIndex: 'product %',
renderer: this.perc
},
{
text : 'Market',
width : 75,
sortable : true,
align: 'right',
dataIndex: 'market %',
renderer: this.perc
}
];
this.callParent(arguments);
}
});
[/code]
View - stocks- StockForm
[code lang="js" firstline="1" toolbar="true" collapse="false" wraplines="false"]
Ext.define('ExtMVC.view.stocks.StockForm', {
extend: 'Ext.form.Panel',
alias : 'widget.stockform',
requires: [
'ExtMVC.view.stocks.StockGrid',
'ExtMVC.view.chart.StockBar',
'ExtMVC.view.chart.StockRadar'
],
title: 'Company data',
frame: true,
bodyPadding: 5,
//width: 870,
//height: 720,
fieldDefaults: {
labelAlign: 'left',
msgTarget: 'side'
},
layout: {
type: 'vbox',
align: 'stretch'
},
items: [
{
height: 200,
layout: 'fit',
margin: '0 0 3 0',
items: [{ xtype: 'stockbar'}]
},
{
layout: {type: 'hbox', align: 'stretch'},
flex: 3,
border: false,
bodyStyle: 'background-color: transparent',
items: [{
xtype: 'stockgrid'
}, {
flex: 0.4,
layout: {
type: 'vbox',
align:'stretch'
},
margin: '0 0 0 5',
title: 'Company Details',
items: [{
margin: '5',
xtype: 'fieldset',
flex: 1,
title:'Company details',
defaults: {
width: 240,
labelWidth: 90,
disabled: true
},
defaultType: 'numberfield',
items: [{
fieldLabel: 'Name',
name: 'company',
xtype: 'textfield'
},{
fieldLabel: 'Price',
name: 'price',
maxValue: 100,
minValue: 0,
enforceMaxLength: true,
maxLength: 5
},{
fieldLabel: 'Revenue %',
name: 'revenue %',
maxValue: 100,
minValue: 0,
enforceMaxLength: true,
maxLength: 5
},{
fieldLabel: 'Growth %',
name: 'growth %',
maxValue: 100,
minValue: 0,
enforceMaxLength: true,
maxLength: 5
},{
fieldLabel: 'Product %',
name: 'product %',
maxValue: 100,
minValue: 0,
enforceMaxLength: true,
maxLength: 5
},{
fieldLabel: 'Market %',
name: 'market %',
maxValue: 100,
minValue: 0,
enforceMaxLength: true,
maxLength: 5
}]
}, {xtype:'stockradar'}]
}]
}]
});
[/code]
ViewPort
[code lang="js" firstline="1" toolbar="true" collapse="false" wraplines="false"]
/**
* The main application viewport, which displays the whole application
* @extends Ext.Viewport
*/
Ext.define('ExtMVC.view.Viewport', {
extend: 'Ext.Viewport',
layout: 'fit',
requires: [
'ExtMVC.view.stocks.StockForm',
'ExtMVC.view.stocks.StockGrid',
'ExtMVC.view.chart.StockBar',
'ExtMVC.view.chart.StockRadar'
],
initComponent: function() {
var me = this;
Ext.apply(me, {
items: [
{
xtype: 'stockform'
}
]
});
me.callParent(arguments);
}
});
[/code]
Controller
[code lang="js" firstline="1" toolbar="true" collapse="false" wraplines="false"]
Ext.define('ExtMVC.controller.Stocks', {
extend: 'Ext.app.Controller',
models: ['Stock','RadarDataSet'],
stores: ['Stocks', 'RadarDataSets'],
views: [
'chart.StockRadar',
'chart.StockBar',
'stocks.StockGrid',
'stocks.StockForm'
],
refs: [{
ref: 'stockForm',
selector: 'form'
},{
ref: 'stockGrid',
selector: 'grid'
}],
init: function() {
this.loadStore();
this.control({
'stockgrid': {
selectionchange: this.selectionchange
}/*,
'textfield': {
change: this.changeField
}*/,
'numberfield': {
change: this.changeField
},
'stockbar': {
afterrender: function (chart,o) {
var series = chart.series.getAt(0);
series.listeners = {
itemmouseup: function(item) {
var series = Ext.ComponentQuery.query('stockbar')[0].series.get(0);
var index = Ext.Array.indexOf(series.items, item);
var selectionModel = Ext.ComponentQuery.query('grid')[0].getSelectionModel();
var selectedStoreItem = item.storeItem;
selectionModel.select(index);
}
}
},
beforerefresh: this.beforerefresh
}
});
},
loadStore: function(){
var store = this.getStocksStore();
// sample static data for the store
var myData = [
['3m Co'],
['Alcoa Inc'],
['Altria Group Inc'],
['American Express Company'],
['American International Group, Inc.'],
['AT&T Inc'],
['Boeing Co.'],
['Caterpillar Inc.'],
['Citigroup, Inc.'],
['E.I. du Pont de Nemours and Company'],
['Exxon Mobil Corp'],
['General Electric Company'],
['General Motors Corporation'],
['Hewlett-Packard Co'],
['Honeywell Intl Inc'],
['Intel Corporation'],
['International Business Machines'],
['Johnson & Johnson'],
['JP Morgan & Chase & Co'],
['McDonald\'s Corporation'],
['Merck & Co., Inc.'],
['Microsoft Corporation'],
['Pfizer Inc'],
['The Coca-Cola Company'],
['The Home Depot, Inc.'],
['The Procter & Gamble Company'],
['United Technologies Corporation'],
['Verizon Communications'],
['Wal-Mart Stores, Inc.']
];
for (var i = 0, l = myData.length, rand = Math.random; i < l; i++) {
var data = myData[i];
data[1] = ((rand() * 10000) >> 0) / 100;
data[2] = ((rand() * 10000) >> 0) / 100;
data[3] = ((rand() * 10000) >> 0) / 100;
data[4] = ((rand() * 10000) >> 0) / 100;
data[5] = ((rand() * 10000) >> 0) / 100;
}
store.loadData(myData);
},
selectionchange: function(model, records){
var json, name, i, l, items, series, fields;
var form = this.getStockForm().getForm();
if (records[0]) {
rec = records[0];
if (!form) {
form = this.getStockForm().getForm();
fields = form.getFields();
fields.each(function(field){
if (field.name != 'company') {
field.setDisabled(false);
}
});
} else {
fields = form.getFields();
}
// prevent change events from firing
fields.each(function(field){
field.suspendEvents();
console.log('suspended');
});
form.loadRecord(rec);
this.updateRecord(rec);
fields.each(function(field){
field.resumeEvents();
});
fields.each(function(field){
if (field.name != 'company') {
field.setDisabled(false);
}
});
}
},
updateRecord: function(rec) {
var name, series, i, l, items, json = [{
'Name': 'Price',
'Data': rec.get('price')
}, {
'Name': 'Revenue %',
'Data': rec.get('revenue %')
}, {
'Name': 'Growth %',
'Data': rec.get('growth %')
}, {
'Name': 'Product %',
'Data': rec.get('product %')
}, {
'Name': 'Market %',
'Data': rec.get('market %')
}];
var store = this.getRadarDataSetsStore();
store.loadData(json);
this.selectItem(rec);
},
selectItem: function(storeItem) {
var name = storeItem.data.company;//storeItem.get('company'),
var series = Ext.ComponentQuery.query('stockbar')[0].series.get(0);
var i, items, l;
series.highlight = true;
series.unHighlightItem();
series.cleanHighlights();
for (i = 0, items = series.items, l = items.length; i < l; i++) {
if (name == items[i].storeItem.get('company')) {
selectedStoreItem = items[i].storeItem;
series.highlightItem(items[i]);
break;
}
}
series.highlight = false;
},
changeField: function(field, newValue, oldValue) {
console.log('changeField');
var form = this.getStockForm().getForm();
var rec = this.getStockGrid().getSelectionModel().getSelection()[0];
if (rec && form) {
if (newValue > field.maxValue) {
field.setValue(field.maxValue);
} else {
form.updateRecord(rec);
this.updateRecord(rec);
}
}
},
beforerefresh: function() {
var timer = false;
return function() {
clearTimeout(timer);
var series = Ext.ComponentQuery.query('stockbar')[0].series.get(0);
var index = Ext.Array.indexOf(series.items, item);
var selectionModel = Ext.ComponentQuery.query('grid')[0].getSelectionModel();
var selectedStoreItem = item.storeItem;
if (selectedStoreItem) {
timer = setTimeout(function() {
this.selectItem(selectedStoreItem);
}, 900);
}
};
}
});
[/code]
App
[code lang="js" firstline="1" toolbar="true" collapse="false" wraplines="false"]
Ext.Loader.setConfig({enabled: true});
Ext.Loader.setPath('Ext.ux', 'app/ux');
/*
* BUG: suspentEvents not honoured in Ext.app.EventBus
*
* note: this fix does not queue events when asked.
*
* http://www.sencha.com/forum/showthread.php?171525
*/
Ext.syncRequire('Ext.app.EventBus');
Ext.override(Ext.app.EventBus, {
constructor: function() {
this.mixins.observable.constructor.call(this);
this.bus = {};
var me = this;
Ext.override(Ext.Component, {
fireEvent: function(ev) {
// [
// --
// if (Ext.util.Observable.prototype.fireEvent.apply(this, arguments) !== false) {
// ++
if (Ext.util.Observable.prototype.fireEvent.apply(this, arguments) !== false && !this.eventsSuspended) {
// ]
return me.dispatch.call(me, ev, this, arguments);
}
return false;
}
});
}
});
Ext.application({
name: 'ExtMVC',
controllers: [
'Stocks'
],
autoCreateViewport: true
});
[/code]
Página HTML
[code lang="html" firstline="1" toolbar="true" collapse="false" wraplines="false"]
<html>
<head>
<title>Ext JS 4 MVC Examples - loiane.com</title>
<!-- Ext JS Files -->
<link rel="stylesheet" type="text/css" href="extjs/resources/css/ext-all.css">
<script type="text/javascript" src="extjs/ext-all-debug.js"></script>
<!-- App Files -->
<script type="text/javascript" src="app.js"></script>
</head>
<body>
</body>
</html>
[/code]
Download do Código Fonte:
Você pode fazer o download do código fonte completo no meu repositório do github: https://github.com/loiane/extjs4-mvc-complex-dashboard
Demo:
Exemplo funcionando: https://loiane.com/extjs/extjs4-mvc-complex-dashboard
Todos os exemplos ExtJS 4 MVC:
http://www.loiane.com/2012/03/exemplos-sencha-extjs-4-em-mvc/
Até o próximo exemplo!