Related
Help with any portion of this is appreciated. I am totally new to using extjs and the version is old (3.3). When the user selects the combo box, data is loaded in the combo box. I need to allow the user to select/insert a blank option (i.e.: so the first option should be id:0 field and can be blank). Last, I must give the model an additional field: id.
I can see the data returned in network tab, so I have the url path correct (the url was set up to return an id and list name), but the store property is empty, so no data in combo box.
header: 'Bucket List',
sortable: true,
dataIndex: 'bucket_list',
width: 10,
align: 'center',
editor: BucketList = new Ext.form.ComboBox({
valueField: 'name',
displayField: 'name',
triggerAction: 'all',
typeAhead: true,
preventMark: true,
editable: false,
store: new Ext.data.Store({
proxy: new Ext.data.HttpProxy({
url: '/todos/sysadmin/bucket-lists',
method: 'GET'
}),
reader: new Ext.data.JsonReader({
root: 'bucket_lists',
fields: [
'id',
'name'
]
}),
listeners: {
beforeload: function (data_objStore, data_objOpt) {
data_objOpt.params.userModel = userModelCbx.getValue();
data_objOpt.params.user_id = 001;
}
}
}),
listeners: {
blur: function () { }
}
}),
Below code shows the response, but at index 0, id is 1. I need index 0 to be id: 0 or null value ( 0: {id: 0, name: ''} )
RESPONSE:
0: {
id: 1,
name: "bucketListItem_1"
}
1: {
id: 2,
name: "bucketListItem_2"
}
2: {
id: 3,
name: "bucketListItem_3"
}
3: {
id: 4,
name: "bucketListItem_4"
}
I have read alot of the docs and answers here on SO. I tried using some of the store methods, like add(), insert(), load(), but I'm probably using them in the wrong places or something. I'm asking here because I'm stuck and I really hope someone will help me. Thanks.
UPDATE:
after beforeload, add this to store listeners to insert a blank record. Make sure your reader is accessing the right fields
beforeload: function( sObj, optObjs ){
// code here...
},
load: function(store, records) {
store.insert(0, [new Ext.data.Record({
id: null,
name: 'None'
})
]);
}
Response:
0: {
id: null,
name: "None"
}
1: {
id: 1,
name: "bucketListItem_1"
}
2: {
id: 2,
name: "bucketListItem_2"
}
...
You may try with next working example. You need to insert the record on store's load listener using new Ext.data.Record. Also check tpl config option - it's needed to show empty record correctly. Example is tested with ExtJS 3.4, but I think it should work with your version too.
Ext.onReady(function() {
var combo = new Ext.form.ComboBox({
tpl : '<tpl for="."><div class="x-combo-list-item">{name} </div></tpl>',
valueField: 'name',
displayField: 'name',
triggerAction: 'all',
typeAhead: true,
preventMark: true,
editable: false,
store: new Ext.data.Store({
proxy: new Ext.data.HttpProxy({
url: '/todos/sysadmin/bucket-lists',
method: 'GET'
}),
reader: new Ext.data.JsonReader({
root: 'bucket_lists',
fields: [
'id',
'name'
]
}),
listeners: {
load: function (s) {
record = new Ext.data.Record({
id: 0,
name: ''
});
s.insert(0, record);
}
}
})
});
var grid = new Ext.grid.EditorGridPanel({
renderTo: Ext.getBody(),
store: new Ext.data.Store({
autoLoad: true,
proxy: new Ext.data.HttpProxy({
url: 'combo-extjs3-editor-json.html',
method: 'GET'
}),
reader: new Ext.data.JsonReader({
root: 'bucket_lists',
fields: [
'id',
'name'
]
})
}),
width: 600,
height: 300,
columns: [
{
header: 'Name',
dataIndex: 'name',
width: 130,
editor: combo
}
]
});
});
I'm trying to filter my JSON that has value of 32 for privilege field. I've tried multiple ways of filtering it but still it's returning me all rows instead the filtered ones.
Ext.onReady(function () {
// create the data store
var store = new Ext.data.Store({
url: url,
reader: new Ext.data.JsonReader({
root: 'keys',
idProperty: 'key',
fields: ['key', 'name']
}),
autoLoad: true
});
store.filter({
property: 'privileges',
value: 32,
type: 'int',
exactMatch: true
});
//create the Grid
var grid = new Ext.grid.GridPanel({
store: store,
columns: [
{
id :'name',
header : 'Name',
width : 500,
sortable : true,
dataIndex: 'name'
},
{
header : 'Key',
width : 500,
sortable : false,
dataIndex: 'key'
},
{
header : 'Regenerate',
width : 300,
dataIndex: ''
}
],
stripeRows: true,
height: 350,
title: 'Administrator Keys'
});
grid.render('restApiKeyContent');
});
Current my JSON is as below
{
"id": "abc123",
"name": "Test",
"queue": "test",
"expire": "2118-11-27T02:05:15.546778Z",
"keys": [
{
"key": "64654asdsad1sd1",
"customer": "Test Customer",
"externalUser": "123213",
"name": "Jenna Testing API Key 1",
"privileges": 31
},
{
"key": "7c5362f98f084ae7b76b8484f660fdcf",
"customer": "Test Customer",
"externalUser": "123213",
"name": "Jenna Testing API Key 1",
"privileges": 16
}
}
Appreciate if someone can guide me on how to filter JSON by a specific field which has the Keys as root?
This may help you. Add privileges field in JSONReader and filter store after store is loaded:
Ext.onReady(function () {
// create the data store
var store = new Ext.data.Store({
url: 'filter-store-extjs3.json',
reader: new Ext.data.JsonReader({
root: 'keys',
idProperty: 'key',
fields: ['key', 'name', 'privileges']
}),
autoLoad: true,
listeners: {
load: function() {
store.filter('privileges', '16', true, true);
}
}
});
var grid = new Ext.grid.GridPanel({
renderTo: Ext.getBody(),
store: store,
columns: [
{
id :'name',
header : 'Name',
width : 500,
sortable : true,
dataIndex: 'name'
},
{
header : 'Key',
width : 500,
sortable : false,
dataIndex: 'key'
},
{
header : 'Regenerate',
width : 300,
dataIndex: ''
}
],
stripeRows: true,
height: 350,
title: 'Administrator Keys'
});
});
I am using Extjs 4.2 grid for my application. In my grid there is an option to select multiple rows and to delete them(via checkbox). But I am but the selected rows not returning any values.
Bellow is my code..
###########################################################################
Ext.Loader.setConfig({enabled: true});
Ext.Loader.setPath('Ext.ux', '../js/extjs_4_2/examples/ux/');
Ext.require([
'Ext.grid.*',
'Ext.data.*',
'Ext.util.*',
'Ext.ux.grid.FiltersFeature',
'Ext.toolbar.Paging',
'Ext.ux.PreviewPlugin',
'Ext.ModelManager',
'Ext.tip.QuickTipManager',
'Ext.selection.CheckboxModel'
]);
Ext.onReady(function(){
Ext.tip.QuickTipManager.init();
Ext.define('ForumThread', {
extend: 'Ext.data.Model',
fields: [
{name: 'patient_name'},
{name: 'referrer_provider'},
{name: 'admit_date'},
{name: 'added_date'},
{name: 'billing_date'},
{name: 'dob'},
{name: 'loc_name'},
{name: 'physician_name'},
{name: 'imploded_diagnosis_name'},
{name: 'imploded_procedure_name'},
{name: 'imploded_optional_name'},
{name: 'imploded_quick_list_name'},
{name: 'med_record_no'},
{name: 'message'}
],
idProperty: 'bill_id'
});
var url = {
remote: '../new_charges_json.php'
};
// configure whether filter query is encoded or not (initially)
var encode = false;
// configure whether filtering is performed locally or remotely (initially)
var local = false;
// create the Data Store
var store = Ext.create('Ext.data.Store', {
pageSize: 10,
model: 'ForumThread',
remoteSort: true,
proxy: {
type: 'jsonp',
url: (local ? url.local : url.remote),
reader: {
root: 'charges_details',
totalProperty: 'total_count'
},
simpleSortMode: true
},
sorters: [{
property: 'patient_name',
direction: 'DESC'
}]
});
var filters = {
ftype: 'filters',
// encode and local configuration options defined previously for easier reuse
encode: encode, // json encode the filter query
local: local, // defaults to false (remote filtering)
// Filters are most naturally placed in the column definition, but can also be
// added here.
filters: [{
type: 'string',
dataIndex: 'patient_name'
}]
};
// use a factory method to reduce code while demonstrating
// that the GridFilter plugin may be configured with or without
// the filter types (the filters may be specified on the column model
var createColumns = function (finish, start) {
var columns = [
{
menuDisabled: true,
sortable: false,
xtype: 'actioncolumn',
width: 50,
items: [{
icon : '../js/extjs_4_2/examples/shared/icons/fam/user_profile.png', // Use a URL in the icon config
tooltip: 'Patient Profile',
renderer: renderTopic,
handler: function(grid, rowIndex, colIndex) {
var rec = store.getAt(rowIndex);
//alert("Bill Id: " + rec.get('bill_id'));
//Ext.Msg.alert('Bill Info', rec.get('patient_name')+ ", Bill Id: " +rec.get('bill_id'));
window.location.href="../newdash/profile.php?bill_id="+rec.get('bill_id');
}
},
]
},
{
dataIndex: 'patient_name',
text: 'Patient Name',
sortable: true,
// instead of specifying filter config just specify filterable=true
// to use store's field's type property (if type property not
// explicitly specified in store config it will be 'auto' which
// GridFilters will assume to be 'StringFilter'
filterable: true
//,filter: {type: 'numeric'}
}, {
dataIndex: 'referrer_provider',
text: 'Referring',
sortable: true
//flex: 1,
}, {
dataIndex: 'admit_date',
text: 'Admit date',
}, {
dataIndex: 'added_date',
text: 'Sign-on date'
}, {
dataIndex: 'billing_date',
text: 'Date Of Service',
filter: true,
renderer: Ext.util.Format.dateRenderer('m/d/Y')
}, {
dataIndex: 'dob',
text: 'DOB'
// this column's filter is defined in the filters feature config
},{
dataIndex: 'loc_name',
text: 'Location',
sortable: true
},{
dataIndex: 'physician_name',
text: 'Physician (BILLED)',
sortable: true
},{
dataIndex: 'imploded_diagnosis_name',
text: 'Diagnosis'
},{
dataIndex: 'imploded_procedure_name',
text: 'Procedure'
},{
dataIndex: 'imploded_optional_name',
text: 'OPT Template'
},{
dataIndex: 'imploded_quick_list_name',
text: 'Quick List'
},{
dataIndex: 'med_record_no',
text: 'Medical Record Number'
},{
dataIndex: 'message',
text: 'TEXT or NOTES'
}
];
return columns.slice(start || 0, finish);
};
var pluginExpanded = true;
var selModel = Ext.create('Ext.selection.CheckboxModel', {
columns: [
{xtype : 'checkcolumn', text : 'Active', dataIndex : 'bill_id'}
],
checkOnly: true,
mode: 'multi',
enableKeyNav: false,
listeners: {
selectionchange: function(sm, selections) {
grid.down('#removeButton').setDisabled(selections.length === 0);
}
}
});
var grid = Ext.create('Ext.grid.Panel', {
//width: 1024,
height: 500,
title: 'Charge Information',
store: store,
//disableSelection: true,
loadMask: true,
features: [filters],
forceFit: true,
viewConfig: {
stripeRows: true,
enableTextSelection: true
},
// grid columns
colModel: createColumns(15),
selModel: selModel,
// inline buttons
dockedItems: [
{
xtype: 'toolbar',
items: [{
itemId: 'removeButton',
text:'Charge Batch',
tooltip:'Charge Batch',
disabled: false,
enableToggle: true,
toggleHandler: function() { // Here goes the delete functionality
alert(selModel.getCount());
var selected = selModel.getView().getSelectionModel().getSelections();
alert(selected.length);
var selectedIds;
if(selected.length>0) {
for(var i=0;i<selected.length;i++) {
if(i==0)
{
selectedIds = selected[i].get("bill_id");
}
else
{
selectedIds = selectedIds + "," + selected[i].get("bill_id");
}
}
}
alert("Seleted Id's: "+selectedIds);return false;
}
}]
}],
// paging bar on the bottom
bbar: Ext.create('Ext.PagingToolbar', {
store: store,
displayInfo: true,
displayMsg: 'Displaying charges {0} - {1} of {2}',
emptyMsg: "No charges to display"
}),
renderTo: 'charges-paging-grid'
});
store.loadPage(1);
});
#
It's giving the numbers in alert dialogue of the selected rows,here
alert(selModel.getCount());
But after this it's throwing a javascript error "selModel.getView is not a function" in the line bellow
var selected = selModel.getView().getSelectionModel().getSelections();
I changed it to var selected = grid.getView().getSelectionModel().getSelections(); Still it's throwing same kind of error(grid.getView is not a function).
Try this
var selected = selModel.getSelection();
Instead of this
var selected = selModel.getSelectionModel().getSelections();
You already have the selection model, why are you trying to ask to get the view to go back to the selection model? It's like doing node.parentNode.childNodes[0].innerHTML.
selModel.getSelection(); is sufficient. Also be sure to read the docs, there's no getView method listed there for Ext.selection.CheckboxModel, so you just made that up!
Ext.require([
'Ext.grid.*',
'Ext.data.*',
'Ext.panel.*'
]);
Ext.onReady(function(){
Ext.define('ImageModel', {
extend: 'Ext.data.Model',
fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date', dateFormat:'timestamp'}]
});
var store = Ext.create('Ext.data.JsonStore', {
model: 'ImageModel',
proxy: {
type: 'ajax',
url: 'get-images.php',
reader: {
type: 'json',
root: 'images'
}
}
});
store.load();
var listView = Ext.create('Ext.grid.Panel', {
width:425,
height:250,
collapsible:true,
title:'Simple ListView <i>(0 items selected)</i>',
renderTo: Ext.getBody(),
store: store,
multiSelect: true,
viewConfig: {
emptyText: 'No images to display'
},
columns: [{
text: 'File',
flex: 50,
dataIndex: 'name'
},{
text: 'Last Modified',
xtype: 'datecolumn',
format: 'm-d h:i a',
flex: 35,
dataIndex: 'lastmod'
},{
text: 'Size',
dataIndex: 'size',
tpl: '{size:fileSize}',
align: 'right',
flex: 15,
cls: 'listview-filesize'
}]
});
// little bit of feedback
listView.on('selectionchange', function(view, nodes){
var l = nodes.length;
var s = l != 1 ? 's' : '';
listView.setTitle('Simple ListView <i>('+l+' item'+s+' selected)</i>');
});
});
i have create 2 panel, one is left panel,second is right panel.
the list view are created in the left panel,and the right panel will read the page showimage.php
list view are display file detail,when user select the list view will passing the file name as a parameter "name" to showimage.php, and the right panel will show the image by name are passed from list view select event.(actually the name field are stored file's ID)
Question
1)how to create the select list view event,when select a list view passing parameter name to showimage.php,and right panel refresh the page and display the image.
//===========================LIST VIEW===============================
Ext.define('ImageModel', {
extend: 'Ext.data.Model',
fields: ['PicID', 'PicUploadedDateTime','PicData']
});
var ImgStore = Ext.create('Ext.data.JsonStore', {
model: 'ImageModel',
autoLoad: true,
proxy: {
type: 'ajax',
url: 'get-image.php',
baseParams: { //here you can define params you want to be sent on each request from this store
mainid: 'value1',
startdate: 'value2',
enddate: 'value3'
},
reader: {
type: 'json',
root: 'images'
}
}
});
var listView = Ext.create('Ext.grid.Panel', {
region: 'west',
id : 'imagelist',
title: 'Select Image',
width: 200,
split: true,
collapsible: true,
floatable: false,
title:'Select Image',
store: ImgStore,
multiSelect: false,
viewConfig: {
emptyText: 'No images to display'
},
columns: [
{
text: 'Date Time Uploaded',
//xtype: 'datecolumn',
//format: 'Y-m-d H:i:s',
flex: 65,
width: 100,
dataIndex: 'PicUploadedDateTime'
}]
});
function hexToBase64(str) {
return btoa(String.fromCharCode.apply(null, str.replace(/\r|\n/g, "").replace(/([\da-fA-F]{2}) ?/g, "0x$1 ").replace(/ +$/, "").split(" ")));
}
listView.on('selectionchange', function(view, nodes){
/*
var nodeIdDisplay = "";
for(var i = 0; i < nodes.length; i++)
{
if(nodeIdDisplay.length > 0)
nodeIdDisplay += ",";
nodeIdDisplay += nodes[i].get("PicID");
}
*/
if (nodes[0].get("PicID") > 0){
//alert(nodes[0].get("PicData"));
image=Ext.getCmp('displayimage');
image.setSrc("data:image/jpeg;base64,"+hexToBase64(nodes[0].get("PicData")));
//console.log(nodes[0].get("PicData"));
}
});
this code method is post to the get-image.php,and get all the image in store and then,
list view on select change event get the image id and display the image on type : 'image' component
How to solve this error Uncaught TypeError: Cannot read property 'dom' of undefined in extjs?
I`m using dnd and put dnd code into layout browser
code :
// Generic fields array to use in both store defs.
var fields = [{
name: 'id',
type: 'string',
mapping: 'id'
}, {
name: 'lab_name',
type: 'string',
mapping: 'lab_name'
}, {
name: 'lab_address1',
type: 'string',
mapping: 'lab_address1'
}, {
name: 'lab_address2',
type: 'string',
mapping: 'lab_address2'
}, {
name: 'lab_poskod',
type: 'string',
mapping: 'lab_poskod'
}, {
name: 'lab_bandar',
type: 'string',
mapping: 'lab_bandar'
}, {
name: 'lab_negeri',
type: 'string',
mapping: 'lab_negeri'
}, {
name: 'lab_tel',
type: 'string',
mapping: 'lab_tel'
}, {
name: 'lab_fax',
type: 'string',
mapping: 'lab_fax'
}];
// create the data store
var gridStore = new Ext.data.JsonStore({
fields: fields,
autoLoad: true,
url: '../industri/layouts/getLab.php'
});
// Column Model shortcut array
var cols = [{
id: 'name',
header: "Id",
width: 10,
sortable: true,
dataIndex: 'id'
}, {
id: 'name',
header: "Laboratory Name",
width: 200,
sortable: true,
dataIndex: 'lab_name'
}, {
id: 'name',
header: "Laboratory Name",
width: 200,
sortable: true,
dataIndex: 'lab_address1'
}];
// declare the source Grid
var grid = new Ext.grid.GridPanel({
ddGroup: 'gridDDGroup',
store: gridStore,
columns: cols,
enableDragDrop: true,
stripeRows: true,
autoExpandColumn: 'name',
width: 325,
margins: '0 2 0 0',
region: 'west',
title: 'Data Grid',
selModel: new Ext.grid.RowSelectionModel({
singleSelect: true
})
});
// Declare the text fields. This could have been done inline, is easier to read
// for folks learning :)
var textField1 = new Ext.form.TextField({
fieldLabel: 'Laboratory Name',
name: 'lab_name'
});
// Setup the form panel
var formPanel = new Ext.form.FormPanel({
region: 'center',
title: 'Generic Form Panel',
bodyStyle: 'padding: 10px; background-color: #DFE8F6',
labelWidth: 100,
margins: '0 0 0 3',
width: 325,
items: [textField1]
});
var displayPanel = new Ext.Panel({
width: 650,
height: 300,
layout: 'border',
padding: 5,
items: [
grid,
formPanel
],
bbar: [
'->', // Fill
{
text: 'Reset Example',
handler: function() {
//refresh source grid
gridStore.loadData();
formPanel.getForm().reset();
}
}
]
});
// used to add records to the destination stores
var blankRecord = Ext.data.Record.create(fields);
/****
* Setup Drop Targets
***/
// This will make sure we only drop to the view container
var formPanelDropTargetEl = formPanel.body.dom;
var formPanelDropTarget = new Ext.dd.DropTarget(formPanelDropTargetEl, {
ddGroup: 'gridDDGroup',
notifyEnter: function(ddSource, e, data) {
//Add some flare to invite drop.
formPanel.body.stopFx();
formPanel.body.highlight();
},
notifyDrop: function(ddSource, e, data) {
// Reference the record (single selection) for readability
var selectedRecord = ddSource.dragData.selections[0];
// Load the record into the form
formPanel.getForm().loadRecord(selectedRecord);
// Delete record from the grid. not really required.
ddSource.grid.store.remove(selectedRecord);
return (true);
}
});
var tabsNestedLayouts = {
id: 'tabs-nested-layouts-panel',
title: 'Industrial Effluent',
bodyStyle: 'padding:15px;',
layout: 'fit',
items: {
border: false,
bodyStyle: 'padding:5px;',
items: displayPanel
}
};
If you try and render a component to a dom element that isn't found (or dom ID that isn't found) you'll get that error. See the example below to reproduce the error - then comment out the bad renderTo and uncomment the renderTo: Ext.getBody() to resolve the issue.
see this FIDDLE
CODE SNIPPET
Ext.onReady(function () {
// Generic fields array to use in both store defs.
var fields = [{
name: 'id',
type: 'string',
mapping: 'id'
}, {
name: 'lab_name',
type: 'string',
mapping: 'lab_name'
}, {
name: 'lab_address1',
type: 'string',
mapping: 'lab_address1'
}, {
name: 'lab_address2',
type: 'string',
mapping: 'lab_address2'
}, {
name: 'lab_poskod',
type: 'string',
mapping: 'lab_poskod'
}, {
name: 'lab_bandar',
type: 'string',
mapping: 'lab_bandar'
}, {
name: 'lab_negeri',
type: 'string',
mapping: 'lab_negeri'
}, {
name: 'lab_tel',
type: 'string',
mapping: 'lab_tel'
}, {
name: 'lab_fax',
type: 'string',
mapping: 'lab_fax'
}];
// create the data store
var gridStore = new Ext.data.JsonStore({
fields: fields,
autoLoad: true,
url: '../industri/layouts/getLab.php'
});
// Column Model shortcut array
var cols = [{
id: 'name',
header: "Id",
width: 10,
sortable: true,
dataIndex: 'id'
}, {
id: 'name',
header: "Laboratory Name",
width: 200,
sortable: true,
dataIndex: 'lab_name'
}, {
id: 'name',
header: "Laboratory Name",
width: 200,
sortable: true,
dataIndex: 'lab_address1'
}];
// declare the source Grid
var grid = new Ext.grid.GridPanel({
ddGroup: 'gridDDGroup',
store: gridStore,
columns: cols,
enableDragDrop: true,
stripeRows: true,
autoExpandColumn: 'name',
width: 325,
margins: '0 2 0 0',
region: 'west',
title: 'Data Grid',
selModel: new Ext.grid.RowSelectionModel({
singleSelect: true
})
});
// Declare the text fields. This could have been done inline, is easier to read
// for folks learning :)
var textField1 = new Ext.form.TextField({
fieldLabel: 'Laboratory Name',
name: 'lab_name'
});
// Setup the form panel
var formPanel = new Ext.form.FormPanel({
region: 'center',
title: 'Generic Form Panel',
bodyStyle: 'padding: 10px; background-color: #DFE8F6',
labelWidth: 100,
margins: '0 0 0 3',
width: 325,
items: [textField1]
});
var displayPanel = new Ext.Panel({
width: 650,
height: 300,
layout: 'border',
renderTo:Ext.getBody(),
padding: 5,
items: [
grid,
formPanel
],
bbar: [
'->', // Fill
{
text: 'Reset Example',
handler: function () {
//refresh source grid
//gridStore.loadData();
formPanel.getForm().reset();
}
}
]
});
// used to add records to the destination stores
var blankRecord = Ext.data.Record.create(fields);
/****
* Setup Drop Targets
***/
// This will make sure we only drop to the view container
var formPanelDropTargetEl = formPanel.body.dom;
var formPanelDropTarget = new Ext.dd.DropTarget(formPanelDropTargetEl, {
ddGroup: 'gridDDGroup',
notifyEnter: function (ddSource, e, data) {
//Add some flare to invite drop.
formPanel.body.stopFx();
formPanel.body.highlight();
},
notifyDrop: function (ddSource, e, data) {
// Reference the record (single selection) for readability
var selectedRecord = ddSource.dragData.selections[0];
// Load the record into the form
formPanel.getForm().loadRecord(selectedRecord);
// Delete record from the grid. not really required.
ddSource.grid.store.remove(selectedRecord);
return (true);
}
});
var tabsNestedLayouts = {
id: 'tabs-nested-layouts-panel',
title: 'Industrial Effluent',
bodyStyle: 'padding:15px;',
layout: 'fit',
items: {
border: false,
bodyStyle: 'padding:5px;',
items: displayPanel
}
};
});
It means that the object which you expect to have the dom attribute is undefined.
EDIT:
The error generates at this line:
formPanel.body.dom
It means that the formPanel is not rendered because you are trying to access its body property. This property is Available since: Ext 4.1.3
I'm seeing a similar error in code that executes for validation. What I'm doing has nothing to do with directly accessing the DOM, however I'm still getting a similar condition. The answer above is incomplete, the dom property is available on some ui elements in 3.x...
in earlier versions of Extjs (3.x) the property is mainBody.dom and not body.dom
directly from the source of hasRows() for grids in 3.4:
var fc = this.**mainBody.dom**.firstChild;
return fc && fc.nodeType == 1 && fc.className != 'x-grid-empty';