DataView and ListView are very useful to render store based data as HTML. This extension adds the ability to include Ext components in the HTML without the need to keep track of them (e.g. you won't get a memory leak when you update or delete a record).
It also supports automatically getting and setting the value from the store if the component is an Ext.form.Field.
Ext.ux.ComponentDataView:
Code:
Ext.ns('Ext.ux');
Ext.ux.ComponentDataView = Ext.extend(Ext.DataView, {
defaultType: 'textfield',
initComponent : function(){
Ext.ux.ComponentDataView.superclass.initComponent.call(this);
this.components = [];
},
refresh : function(){
Ext.destroy(this.components);
this.components = [];
Ext.ux.ComponentDataView.superclass.refresh.call(this);
this.renderItems(0, this.store.getCount() - 1);
},
onUpdate : function(ds, record){
var index = ds.indexOf(record);
if(index > -1){
this.destroyItems(index);
}
Ext.ux.ComponentDataView.superclass.onUpdate.apply(this, arguments);
if(index > -1){
this.renderItems(index, index);
}
},
onAdd : function(ds, records, index){
var count = this.all.getCount();
Ext.ux.ComponentDataView.superclass.onAdd.apply(this, arguments);
if(count !== 0){
this.renderItems(index, index + records.length - 1);
}
},
onRemove : function(ds, record, index){
this.destroyItems(index);
Ext.ux.ComponentDataView.superclass.onRemove.apply(this, arguments);
},
onDestroy : function(){
Ext.ux.ComponentDataView.onDestroy.call(this);
Ext.destroy(this.components);
this.components = [];
},
renderItems : function(startIndex, endIndex){
var ns = this.all.elements;
var args = [startIndex, 0];
for(var i = startIndex; i <= endIndex; i++){
var r = args[args.length] = [];
for(var items = this.items, j = 0, len = items.length, c; j < len; j++){
c = items[j].render ?
c = items[j].cloneConfig() :
Ext.create(items[j], this.defaultType);
r[j] = c;
if(c.renderTarget){
c.render(Ext.DomQuery.selectNode(c.renderTarget, ns[i]));
}else if(c.applyTarget){
c.applyToMarkup(Ext.DomQuery.selectNode(c.applyTarget, ns[i]));
}else{
c.render(ns[i]);
}
if(Ext.isFunction(c.setValue) && c.applyValue){
c.setValue(this.store.getAt(i).get(c.applyValue));
c.on('blur', function(f){
this.store.getAt(this.index).data[this.dataIndex] = f.getValue();
}, {store: this.store, index: i, dataIndex: c.applyValue});
}
}
}
this.components.splice.apply(this.components, args);
},
destroyItems : function(index){
Ext.destroy(this.components[index]);
this.components.splice(index, 1);
}
});
Ext.reg('compdataview', Ext.ux.ComponentDataView);
Ext.ux.ComponentListView:
Code:
Ext.ux.ComponentListView = Ext.extend(Ext.ListView, {
defaultType: 'textfield',
initComponent : function(){
Ext.ux.ComponentListView.superclass.initComponent.call(this);
this.components = [];
},
refresh : function(){
Ext.destroy(this.components);
this.components = [];
Ext.ux.ComponentListView.superclass.refresh.apply(this, arguments);
this.renderItems(0, this.store.getCount() - 1);
},
onUpdate : function(ds, record){
var index = ds.indexOf(record);
if(index > -1){
this.destroyItems(index);
}
Ext.ux.ComponentListView.superclass.onUpdate.apply(this, arguments);
if(index > -1){
this.renderItems(index, index);
}
},
onAdd : function(ds, records, index){
var count = this.all.getCount();
Ext.ux.ComponentListView.superclass.onAdd.apply(this, arguments);
if(count !== 0){
this.renderItems(index, index + records.length - 1);
}
},
onRemove : function(ds, record, index){
this.destroyItems(index);
Ext.ux.ComponentListView.superclass.onRemove.apply(this, arguments);
},
onDestroy : function(){
Ext.ux.ComponentDataView.onDestroy.call(this);
Ext.destroy(this.components);
this.components = [];
},
renderItems : function(startIndex, endIndex){
var ns = this.all.elements;
var args = [startIndex, 0];
for(var i = startIndex; i <= endIndex; i++){
var r = args[args.length] = [];
for(var columns = this.columns, j = 0, len = columns.length, c; j < len; j++){
var component = columns[j].component;
c = component.render ?
c = component.cloneConfig() :
Ext.create(component, this.defaultType);
r[j] = c;
var node = ns[i].getElementsByTagName('dt')[j].firstChild;
if(c.renderTarget){
c.render(Ext.DomQuery.selectNode(c.renderTarget, node));
}else if(c.applyTarget){
c.applyToMarkup(Ext.DomQuery.selectNode(c.applyTarget, node));
}else{
c.render(node);
}
if(c.applyValue === true){
c.applyValue = columns[j].dataIndex;
}
if(Ext.isFunction(c.setValue) && c.applyValue){
c.setValue(this.store.getAt(i).get(c.applyValue));
c.on('blur', function(f){
this.store.getAt(this.index).data[this.dataIndex] = f.getValue();
}, {store: this.store, index: i, dataIndex: c.applyValue});
}
}
}
this.components.splice.apply(this.components, args);
},
destroyItems : function(index){
Ext.destroy(this.components[index]);
this.components.splice(index, 1);
}
});
Ext.reg('complistview', Ext.ux.ComponentListView);
Usage example:
Code:
Ext.onReady(function() {
new Ext.Viewport({
layout: 'hbox',
layoutConfig: {
align: 'stretch'
},
defaults: {
flex: 1
},
items: [{
title: 'ComponentDataView example',
items: {
xtype: 'compdataview',
store: [[1, 'One'], [2, 'Two'], [3, 'Three']],
itemSelector: 'tbody tr',
tpl: '<table><thead><tr><td>Value</td><td>Text</td></tr></thead><tbody><tpl for="."><tr><td></td><td></td></tr></tpl></tbody></table>',
items: [{
xtype: 'numberfield',
minValue: 0,
maxValue: 100,
renderTarget: 'td:nth(1)',
applyValue: 'field1'
},{
allowBlank: false,
renderTarget: 'td:nth(2)',
applyValue: 'field2'
}]
}
},{
title: 'ComponentListView example',
items: {
xtype: 'complistview',
store: [[1, 'One'], [2, 'Two'], [3, 'Three']],
columns: [{
header: 'Value',
width: .5,
dataIndex: 'field1',
tpl: ' ',
component: {
xtype: 'numberfield',
minValue: 0,
maxValue: 100,
applyValue: true
}
},{
header: 'Text',
width: .5,
dataIndex: 'field2',
tpl: ' ',
component: {
allowBlank: false,
applyValue: true
}
}]
}
}]
});
});