Tutorial ExtJS 4: Grid Mestre-Detalhe usando plugin RowExpander e Model Associations

No post de hoje vamos aprender como implementar um grid em ExtJS 4 usando Model Associations, e por isso, o grid sera do tipo Mestre-Detalhe, e para isso vamos usar também um plugin chamado RowExpander (com algumas modificações para atender a esse requisito).

Este é um dos tutoriais mais pedidos que recebo por email. Vamos então implementá-lo. No post de hoje vou mostrar a maneira mais simples possível de fazer isso. Na verdade vou usar uma solução que já existe e apenas replicar aqui no blog.

Requisitos:

Passos:

  1. Declarar os Models com Associações
  2. Criar dados de teste – json
  3. Criar os Grids

1 – Models com Associações

[code lang="js" firstline="1" toolbar="true" collapse="false" wraplines="false"]
Ext.define('Company', {
extend : 'Ext.data.Model',
idProperty : 'companyId',
fields : [
'company',
'price',
'change',
'pctChange',
{ name : 'lastChange', type : 'date', dateFormat : 'n/j g:ia' }
],

proxy : {
type : 'ajax',
url : 'hasMany.json',
reader : {
type : 'json',
root : 'data'
}
},

hasMany : {
model : 'History',
name : 'history'
}
});

Ext.define('History', {
extend : 'Ext.data.Model',
fields : [
{ name : 'date', type : 'date', dateFormat : 'n/j g:ia' },
'text',
'companyId'
],

proxy : {
type : 'ajax',
url : 'belongsTo.json',
reader : {
type : 'json',
root : 'data'
}
},

belongsTo : {
model : 'Company',
name : 'companies',
foreignKey : 'companyId'
},

hasOne : {
model : 'Company',
name : 'companies',
foreignKey : 'companyId',
getterName : 'getCompanyOne'
}
});
[/code]

2 – Dados de Teste – json

2.1 - HasMany – json

[code lang="js" firstline="1" toolbar="true" collapse="false" wraplines="false"]
{
"data": [
{
"companyId": 1,
"company": "3m Co",
"price": 71.72,
"change": -0.02,
"pctChange": -0.03,
"lastChange": "9/1 12:00am",
"history" : [
{ "date" : "9/1 12:00am", "text" : "Test" },
{ "date" : "8/1 12:00am", "text" : "Initial" }
]
},
{
"companyId": 2,
"company": "Alcoa Inc",
"price": 29.01,
"change": 0.42,
"pctChange": 1.47,
"lastChange": "9/1 12:00am",
"history" : [
{ "date" : "9/1 12:00am", "text" : "Test" },
{ "date" : "8/1 12:00am", "text" : "Initial" }
]
},
{
"companyId": 3,
"company": "Altria Group Inc",
"price": 83.81,
"change": 0.28,
"pctChange": 0.34,
"lastChange": "9/1 12:00am",
"history" : [
{ "date" : "9/1 12:00am", "text" : "Test" },
{ "date" : "8/1 12:00am", "text" : "Initial" }
]
}
]
}
[/code]

2.2 - BelongsTo – json

[code lang="js" firstline="1" toolbar="true" collapse="false" wraplines="false"]
{
"data": [
{
"date": "9/1 12:00am",
"text": "Test",
"companyId": 1
},
{
"date": "8/1 12:00am",
"text": "Initial",
"companyId": 2
}
]
}
[/code]

3 – Criação dos Grids

3.1 - HasMany – com Grid

[code lang="js" firstline="1" toolbar="true" collapse="false" wraplines="false"]
Ext.create('Ext.grid.Panel',{
store : Ext.create('Ext.data.Store',{
model : 'Company',
autoLoad : true,
proxy : {
type : 'ajax',
url : 'hasMany.json',
reader : {
type : 'json',
root : 'data'
}
}
}),
renderTo : 'hasManyGrid',
width : 600,
height : 300,
columns : [
{ text : 'Company', dataIndex : 'company', flex : 1 },
{ text : 'Price', dataIndex : 'price', renderer : Ext.util.Format.usMoney },
{ text : 'Change', dataIndex : 'change' },
{ text : '% Change', dataIndex : 'pctChange' },
{ text : 'Last Updated', dataIndex : 'lastChange', renderer : Ext.util.Format.dateRenderer('m/d/Y') }
],
plugins : [
{
ptype : 'associationrowexpander',
getterName : 'history',
gridConfig : {
height : 100,
title : 'History',
columns : [
{
header : 'Text',
dataIndex : 'text',
flex : 1
},
{
header : 'Date',
dataIndex : 'date',
width : 200,
renderer : Ext.util.Format.dateRenderer('n/j g:ia')
}
]
}
}
]
});
[/code]

3.2 - HasMany – com View

[code lang="js" firstline="1" toolbar="true" collapse="false" wraplines="false"]
Ext.create('Ext.grid.Panel',{
store : Ext.create('Ext.data.Store',{
model : 'Company',
autoLoad : true,
proxy : {
type : 'ajax',
url : 'hasMany.json',
reader : {
type : 'json',
root : 'data'
}
}
}),
renderTo : 'hasManyView',
width : 600,
height : 300,
columns : [
{ text : 'Company', dataIndex : 'company', flex : 1 },
{ text : 'Price', dataIndex : 'price', renderer : Ext.util.Format.usMoney },
{ text : 'Change', dataIndex : 'change' },
{ text : '% Change', dataIndex : 'pctChange' },
{ text : 'Last Updated', dataIndex : 'lastChange', renderer : Ext.util.Format.dateRenderer('m/d/Y') }
],
plugins : [
{
ptype : 'associationrowexpander',
getterName : 'history',
viewConfig : {
itemSelector : 'div.history-text',
emptyText : 'There is no history',
tpl : new Ext.XTemplate(
'<div><b>History</b></div>',
'<tpl for=".">',
'<div class="history-text">{text} ({date:date("n/j g:ia")})</div>',
'</tpl>'
)
}
}
]
});
[/code]

3.3 - BelongsTo – com XTemplate

[code lang="js" firstline="1" toolbar="true" collapse="false" wraplines="false"]
Ext.create('Ext.grid.Panel',{
store : Ext.create('Ext.data.Store',{
model : 'History',
autoLoad : true
}),
renderTo : 'belongsTo',
width : 600,
height : 300,
columns : [
{ text : 'Text', dataIndex : 'text', flex : 1 },
{ text : 'Date', dataIndex : 'date', renderer : Ext.util.Format.dateRenderer('m/d/Y') }
],
plugins : [
{
ptype : 'associationrowexpander',
type : 'belongsTo',
getterName : 'getCompany',
rowBodyTpl : new Ext.XTemplate(
'<div><b>Company Details:</b></div>',
'<div>{company} - {[this.colorVal(values.price, true)]}</div>',
'<div>{lastChange:date("n/j g:ia")}</div>',
'<div>{[this.colorVal(values.change, true)]} {[this.colorVal(values.pctChange, false)]}</div>',
{
colorVal : function(value, money) {
var color = value === 0 ? '000' : (value > 0 ? '093' : 'F00');

if (money) {
value = Ext.util.Format.usMoney(value);
} else {
value += '%';
}

return '<span style="color: #' + color + ';">' + value + '</span>';
}
}
)
}
]
});
[/code]

3.4 - HasOne – com XTemplate

[code lang="js" firstline="1" toolbar="true" collapse="false" wraplines="false"]
Ext.create('Ext.grid.Panel',{
store : Ext.create('Ext.data.Store',{
model : 'History',
autoLoad : true
}),
renderTo : 'hasOne',
width : 600,
height : 300,
columns : [
{ text : 'Text', dataIndex : 'text', flex : 1 },
{ text : 'Date', dataIndex : 'date', renderer : Ext.util.Format.dateRenderer('m/d/Y') }
],
plugins : [
{
ptype : 'associationrowexpander',
type : 'hasOne',
getterName : 'getCompanyOne',
rowBodyTpl : new Ext.XTemplate(
'<div><b>Company Details:</b></div>',
'<div>{company} - {[this.colorVal(values.price, true)]}</div>',
'<div>{lastChange:date("n/j g:ia")}</div>',
'<div>{[this.colorVal(values.change, true)]} {[this.colorVal(values.pctChange, false)]}</div>',
{
colorVal : function(value, money) {
var color = value === 0 ? '000' : (value > 0 ? '093' : 'F00');

if (money) {
value = Ext.util.Format.usMoney(value);
} else {
value += '%';
}

return '<span style="color: #' + color + ';">' + value + '</span>';
}
}
)
}
]
});
[/code]

Download

Código Fontehttps://github.com/mitchellsimoens/Ux.grid.plugin.AssociationRowExpander

Até a próxima! :)