Extjs Dynamic Grid - extjs

I'm trying to create a dynamic grid using ExtJS. The grid is built and displayed when a click event is fired then an ajax request is sent to the server to fetch the columns, records and records definition a.k.a store fields.
Each node could have different grid structure and that depends on the level of the node in the tree.
The only way I came up with so far is :
function showGrid(response, request) {
var jsonData = Ext.util.JSON.decode(response.responseText);
var grid = Ext.getCmp('contentGrid' + request.params.owner);
if (grid) {
grid.destroy();
}
var store = new Ext.data.ArrayStore({
id: 'arrayStore',
fields: jsonData.recordFields,
autoDestroy: true
});
grid = new Ext.grid.GridPanel({
defaults: {
sortable: true
},
id: 'contentGrid' + request.params.owner,
store: store,
columns: jsonData.columns,
//width:540,
//height:200,
loadMask: true
});
store.loadData(jsonData.records);
if (Ext.getCmp('tab-' + request.params.owner)) {
Ext.getCmp('tab-' + request.params.owner).show();
} else {
grid.render('grid-div');
Ext.getCmp('card-tabs-panel').add({
id: 'tab-' + request.params.owner,
title: request.params.text,
iconCls: 'silk-tab',
html: Ext.getDom('grid-div').innerHTML,
closable: true
}).show();
}
}
The function above is called when a click event is fired
'click': function(node) {
Ext.Ajax.request({
url: 'showCtn',
success: function(response, request) {
alert('Success');
showGrid(response, request);
},
failure: function(results, request) {
alert('Error');
},
params: Ext.urlDecode(node.attributes.options);
}
});
}
The problem I'm getting with this code is that a new grid is displayed each time the showGrid function is called. The end user sees the old grids and the new one. To mitigate this problem, I tried destroying the grid and also removing the grid element on each request, and that seems to solve the problem only that records never get displayed this time.
if (grid) {
grid.destroy(true);
}
The behaviour I'm looking for is to display the result of a grid within a tab and if that tab exists replaced the old grid.
Any help is appreciated.

When you are trying to add your grid to the tab like this:
html:Ext.getDom('grid-div').innerHTML,
Ext is not aware of it being a valid grid component. Instead, you are simply adding HTML markup that just happens to look like a grid, but the TabPanel will not be aware that it is a grid component.
Instead you should add the grid itself as the tab (a GridPanel is a Panel and does not need to be nested into a parent panel). You can do so and also apply the needed tab configs like this:
Ext.getCmp('card-tabs-panel').add({
Ext.apply(grid, {
id: 'tab-' + request.params.owner,
title: request.params.text,
iconCls: 'silk-tab',
closable: true
});
}).show();
BTW, constantly creating and destroying grids is not an ideal strategy if you can avoid it. It might be better to simply hide and re-show grids (and reload their data) based on which type of grid is needed if that's possible (assuming the set of grid types is finite).

A potential option is to use the metaData field on the JsonStore that allows dynamic reconfiguring of the grid columns as per new datasets.
From
One of the most helpful blog posts about this that Ive found is this one:
http://blog.nextlogic.net/2009/04/dynamic-columns-in-ext-js-grid.html and the original info is well documented at http://docs.sencha.com/ext-js/3-4/#!/api/Ext.data.JsonReader

Related

Need to set class/id values on buttons in Extjs MessageBox

Our testing team require IDs or class values to be set on the HTML elements in our message popup boxes. This is for their automated tests.
I can pass in a class value for the dialog panel by passing in a cls value like so:
Ext.Msg.show({
title:'Reset Grid Layout',
msg: 'Are you sure that you want to reset the grid layout?',
cls:'Reset-Grid-Layout-Message',
buttons: Ext.Msg.YESNO,
fn: function (response) {
if (response == 'yes') {
}
},
icon: Ext.window.MessageBox.QUESTION
});
Now we also need it on the buttons, and also on the text being displayed. Is there some way of getting a cls value onto the buttons?
I was thinking it may be possible to expand the button parameter into something like :
buttons : [{name:'but1', cls:'asdf'}, {name:'but2', cls:'asdf2'}]
But google is not giving me back anything useful.
If your testing team uses Selenium for their automated test, adding ids/classes in every component could be difficult for both of you.
Overriding components in Ext is a good solution, but I don't recommend this because it will affect all your components. Unless you know what you're doing.
I suggest, extend Ext.window.MessageBox and generate classes for your buttons based on your parent cls.
// Put this somewhere like /custom/messagebox.js
Ext.define('App.MyMessageBox', {
extend: 'Ext.window.MessageBox'
,initConfig: function(config) {
this.callParent(arguments);
}
,makeButton: function(btnIdx) {
var me = this;
var btnId = me.buttonIds[btnIdx];
return new Ext.button.Button({
handler: me.btnCallback
,cls: me.cls + '-' + btnId
,itemId: btnId
,scope: me
,text: me.buttonText[btnId]
,minWidth: 75
});
}
});
To use:
App.Box = new App.MyMessageBox({
cls:'reset-grid-layout'
}).show({
title:'Reset Grid Layout'
,msg: 'Are you sure that you want to reset the grid layout?'
,buttons: Ext.Msg.YESNO
,icon: Ext.window.MessageBox.QUESTION
});
Your buttons will have reset-grid-layout-yes and reset-grid-layout-no class.
You can do the same with other components you have. Check out the Fiddle. https://fiddle.sencha.com/#fiddle/7qb
You should refer to the API
cls : String A CSS class string to apply to the button's main element.
Overrides: Ext.AbstractComponent.cls
You can also use the filter on right side (not the one in the right top corner). Just type cls and you will see all properties, methods and events containing cls (note that you see by default just public members, use the menu on the right of this searchfield to extend this)
Edit
If you just need it for testing purpose I would recommend to override the responsible method. This should work (untested!)
Ext.window.MessageBox.override({
buttonClasses: [
'okCls', 'yesCls', 'noCls', 'cancelCls'
],
makeButton: function(btnIdx) {
var btnId = this.buttonIds[btnIdx];
var btnCls = this.buttonClasses[btnIdx];
return new Ext.button.Button({
handler: this.btnCallback,
cls: btnCls,
itemId: btnId,
scope: this,
text: this.buttonText[btnId],
minWidth: 75
});
}
});

ExtJS 4.2.1 Grid view overriden with custom XTemplate shows nothing

I am working on a switch from version 4.1.1 to 4.2.1 and finally I need to fix hopefully the last bug. We have a gridview with view being overriden by simple (?) Ext.XTemplate, as follows:
Ext.define('view.monitoring.Event',
{
extend: 'Ext.view.View',
alias: 'widget.default_monitoring_event',
itemSelector: '.monitoring-thumb-fumb',
tplWriteMode: 'overwrite',
autoWidth: true,
initComponent: function()
{
var tplPart1 = new Ext.XTemplate('<SOME HTML 1>');
var tplPart2 = new Ext.XTemplate('<SOME HTML 2>');
var tplPart3 = new Ext.XTemplate('<SOME HTML 3>');
var tplPart4 = new Ext.XTemplate('<SOME HTML 4>');
this.tpl = new Ext.Template('<BUNCH OF HTML AND TPL and TPL INSIDE TPL',
callFunc1: function(data) { /* do something with tplPart1 */ },
callFunc2: function(data) { /* do something with tplPart2 */ },
callFunc3: function(data) { /* do something with tplPart3 */ },
callFunc4: function(data) { /* do something with tplPart4 */ },
);
this.callParent(arguments;
}
}
This view is set for the grid in this way:
Ext.define('view.monitoring.WeeklyOverview',
{
extend: 'Ext.grid.Panel',
alias: 'widget.default_monitoring_weeklyoverview',
layout: 'border',
title: Lang.translate('event_monitoring_title'),
overflowX: 'hidden',
overflowY: 'auto',
initComponent: function()
{
//...
this.view = Ext.widget('default_monitoring_event', {
store: Ext.create('store.monitoring.Rows')
});
//...
}
}
Then in controller onRender the stores gets populated (the store of the grid is AJAX, loads the data and passes them to a memory store of the view). In ExtJS 4.1.1 this is working properly, data are loaded and the template is parsed and HTML is displayed containing the data.
But after the switch to 4.2.1 the HTML is no longer filled with the data and nothing is displayed but an empty HTML table (which appears as like nothing would be rendered). As there is at least some part of HTML but no data, I guess the problem might be with the data being applied for the template.
Does anybody know what might be/went wrong?
UPDATE: After debugging and the custom view template simplification I have found out, that even the custom view has set it's own store with it's own root for the data returned, it ignores that setting and simply loads the data for the store of the Ext.grid.Panel component. The AJAX response looks like:
{
user: {},
data: [
0: { ... },
1: { ... },
...
],
rows: [
0: { ... },
1: { ... },
...
]
}
Here the data should be used for Ext.grid.Panel component and the rows should be then populated by the custom view (configured with Ext.XTemplate). Seems like for the children views always the parent's store's root element is returned. Any way how to workaround this behaviour and make the custom view to use it's own store???
Finally found a solution.
The problem was that after the main grid.Panel loaded the data it distributed them also to it's (sub)view. After that even I had manually loaded the right data into this custom XTemplate, it needed to also manually override previously rendered view.
In my controller (init -> render) I needed to do this:
var me = this;
this.getMainView().store.load({
params: params || {},
callback: function(records, operation, success) {
// this line was already there, working properly for ExtJS 4.1.1, probably is now useless for ExtJS 4.2.1 with the next line
me.getCustomView().store.loadRawData(this.proxy.reader.rawData);
// I had to add this line for ExtJS 4.2.1...
me.getCustomView().tpl.overwrite(me.getCustomView().el, this.proxy.reader.rawData.rows);
}
});
This is kinda strange as I thought that with a definition within a custom view (see my question above)
tplWriteMode: 'overwrite',
it should just automatically overwrite it's view...

show dynamic data using checkbox in extjs

I have a combo box in which the user selects a value, that value is passed to the checkbox data store and it is populated dynamically from database (oracle). I tried the code below. It seems that the selected parameter is being passed to checkbox and I can see the data being populated on the console. I just can't render the checkbox on form. The error I get is: typeError: this.items[0] is undefined.
testArray = new Array();
var testStore = new Ext.data.JsonStore({
proxy:new Ext.data.HttpProxy({
method:'GET',
prettyUrls:false,
url:'kiu.htm',
listeners:{
'loadexception':{
fn:test.form.data.loadException
}
}
}),
fields:["id", "display"],
reader:new Ext.data.JsonReader({
id:'id',
root:'results',
totalProperty:'totalCount',
fields:new Ext.data.Record.create([
{name:'id',type:'int'},
{name:'display',type:'string'}
])
}),
listeners:{
load: function(t, records, options, success) {
for(var i=0; i<records.length; i++) {
testArray.push({name:records[i].data.id, boxLabel: records[i].data.display});
alert(testArray[i].id);
}
}
}
});
{
xtype:'combo',
id:'comboid3',
store:combostore,
displayField:'display',
valueField:'id',
tabIndex:1,
loadingText:'Loading combo...',
listeners :{
select:function(event){
testStore.baseParams = {
"comboid":Ext.getCmp('comboid3').getValue()
};
testStore.load();
}
}
},
{
xtype:'checkboxgroup',
fieldLabel:'Check',
items:testArray
}
Help will be appreciated!
Specifying the Ext JS version is always going to be helpful. It appears this must be 2.x or 3.x and not the current version.
The issue is timing, load calls are asynchronous so by attempting to utilize testArray in this fashion you are likely referencing an empty array by the time it parses the items property of your checkboxgroup. You have a couple options around this... one is to grab a reference to the checkbox group and add the items into it, the other is to not put the checkbox group in the form at all until the call returns and then add it with the populated items array. In either case it is likely that you will need to look up a component reference to either the FormPanel or the CheckboxGroup from within the load handler function and call the 'add' method to add child items.

ExtJS extend grid RowEditor plugin (to edit array)

I've been using the ExtJS grid row editing plugin pretty liberally for CRUD operations in web applications. Now, I have a requirement to allow a database record to be edited along with a related collection/array (from another datastore) using this row editing plugin.
To do this I want to insert drag-n-drop grids inside of a row that has been selected for editing, one grid showing the available (unused) collection items on the left and another grid to hold the defined collection items on the right.
To illustrate what I am trying to do, here is the normal row editing plugin with a row selected for editing:
I am trying to do this (drag-n-drop grids inside of row editor div):
To do this I have been trying to simply run something like Ext.getCmp(???).add(myDnDGridPanel); but I haven't found the right thing to attach this to (what to put in the question marks).
It seems reasonable enough to use this plugin to edit the related collection/array along with the database record. Does anyone know a simple way to add items to this row editor div?
Or... will I have to hack/extend the plugin to accomplish this?
Below is example RowEditing plugin modification which allows to add additional components. In this demo this is only a button, but it should be easy to customize.
var rowEditing = Ext.create('Ext.grid.plugin.RowEditing', {
clicksToMoveEditor: 1,
autoCancel: false,
listeners: {
beforeedit: function(editor, e, eOpts) {
var body = this.editor.body;
var container = body.down('.container-for-extra-content');
if (!container) {
container = Ext.core.DomHelper.insertAfter(body.last(), '<div class="container-for-extra-content"></div>', true);
container.setWidth(this.editor.body.getWidth(true));
container.setHeight(this.extraHeight);
this.editor.getEl().setHeight(this.editor.getEl().getHeight() + this.extraHeight);
this.editor.body.setHeight(this.editor.body.getHeight() + this.extraHeight);
var panelConfig = {
renderTo: container,
items: [this.extraComponent]
};
Ext.create('Ext.Panel', panelConfig);
}
},
delay: 1
},
extraHeight: 100,
extraComponent: {
xtype: 'panel',
items: [
{ xtype: 'button', text: 'Aloha!' }
]
}
});
Here is working sample: http://jsfiddle.net/e2DzY/1/

Does Ext.view.View support the autoScroll config option?

I've been unable to add Scroll Bars to two views I have displaying with Ext. Each View is an extension of a base view, and the base has autoScroll: true. I've verified that the elements are properly assigned an overflow: auto property.
I've done some searching around and found this question and after applying it's solution (even though I wasn't using this 'vbox' layout) it didn't solve my problem.
Here is the base view:
Ext.define('Our.extensions.baseList', {
extend: 'Ext.view.View',
itemSelector: 'div.postContainer',
autoScroll: true,
initComponent: function(){
this.tpl = new Ext.XTemplate(
'<tpl for=".">',
'<div name="postContainer" class="postContainer">',
// Contains Layout for posts
'</div>',
{ // can run internal functions within a template
createButton: function(idPost){
// Creates layout for buttons and returns it
}
}
);
this.callParent(arguments);
}
});
And the two child views are:
Ext.define('Our.view.post.convList', {
extend: 'Our.extensions.baseList',
alias : 'widget.convList',
emptyText: 'No Conversation Followed',
title: 'Coversations',
initComponent: function(){
this.store = new Ext.create('Ext.data.Store', {
id:'convStore',
model: 'Our.model.Post',
data: []
});
this.callParent(arguments);
}
});
And:
Ext.define('Our.view.post.postList', {
extend: 'Our.extensions.baseList',
alias : 'widget.postList',
emptyText: 'No Posts',
title: 'Posts'
});
Ultimately, my question is does Ext.view.View actually support the autoScroll config property (or does it just ignore it as it seems to), which is listed in the Sencha docs as a valid config property. I know that it doesn't support the layout config hence why the linked questions solution failed to work. If it does support it, how would I go about getting the scroll bars to show up?
EDIT
Some things I've discovered in my attempts to come up with a solution, setting a size on the View itself works as long as the size is hard coded (a set number of pixels). Such as adding the config: height: 500, or a CSS class with height: 500px; but percentage sizes through CSS classes don't work at all - they don't even size the component.
SECOND EDIT
As I stated in comments to the first answer received, the component was getting the size 1063 (a note is that, the development environment is on a 1080x1920 monitor (in portrait) and with the JavaScript Console running in Chrome it's the height of the visible frame is 1063 pixels. By 'visible' I mean the postPanel that the Views are being added to.
I have found a temporary solution, unfortunately I don't think it's ideal for an actual production release - I'm using the refresh of the XTemplate to set the height of the component to the height of the panel to that of the postPanel (which sets the same height as it has been receiving) and this seems to force it to apply the scroll bars. I also added a listener to the resize event of the postPanel that fires the refresh event of the two Views.
The additions...
to the child views:
listeners: {
refresh: function() {
var parent = this.up('postPanel');
if (parent != undefined) {
this.setSize({
width: undefined,
height: parent.getHeight()
});
}
}
}
and to the postPanel:
listeners: {
resize: function() {
var postList = this.down('postList');
if (postList != undefined) {
try {
postList.fireEvent('refresh');
} catch(e) { /* ignored */ }
}
var convList = this.down('convList');
if (convList != undefined) {
try {
convList.fireEvent('refresh');
} catch(e) { /* ignored */ }
}
}
}
The main reason I feel this is not an ideal solution is that it's broken the views automatic resizing to half the width of the parent (and setting the width to parent.getWidth() / 2 in the this.setSize() call in the refresh event doesn't alter the size at all, even if the parent is resized. In addition to that, I'm not sure if this will add any overhead to slower computers or not.
So the autoScroll should work as long as the component knows the size of the box it is supposed to fit in. If you don't want to set a specific box size try placing your view component into a viewport with fit layout. Then the viewport will dictate the size of the box and your view should start scrolling in the allocated space. This principal is used for all containers - some one somwhere must specify the size of the box. Viewport is the magic component that figures out the size of the full screen by default.
Try to place each of your views in a container with layout: 'fix'. Then the autoScroll: true of the views will force the scroll bars, when the content requires it.

Resources