Ronaldo
8 Aug 2007, 2:38 PM
Hey,
While learning ext, I read some posts about the fact that you, that is, the developer, needs to make sure that components are destroyed properly if you do not need a component any more. Otherwise, the browser will consume more and more memory.
As I intented to use a single index page with the menu and the extjs lib etc, and then reload partial pages with the requested content, I needed a general way to dispose of the components being displayed before refreshing the page.
I though I might share it as well. This is what I've come up with:
The index page has the page layout, a content div (in which the requested pages need to be loaded) and a menu.js with a mainmenu definition:
Ext.onReady(function(){
var tb = new Ext.Toolbar('mainmenu');
tb.add(
new Ext.Toolbar.Button({
text: 'Users',
handler: loadContent('/HDD/crud/browseUsers.do')
}),
new Ext.Toolbar.Button({
text: 'Companies',
handler: loadContent('/HDD/crud/browseCompanies.do')
})
);
});
Notice that the handler is defined as a global function in the same menu.js file:
function loadContent(url) {
return function(){
Ext.get("content").load({
url: url,
scripts:true,
text: "Loading..."
});
}
}
Now as a user clicks the Companies button, a jsp page with a grid div is loaded, and a grid is created etc etc. Works fine. But on switching to another page, like the Users page, the old Company grid remains in memory. So before switching to another page, in the loadContent function, I call destroyPool.destroy().
function loadContent(url) {
return function(){
destroyPool.destroy();
Ext.get("content").load({
url: url,
scripts:true,
text: "Loading..."
});
}
}
The 'destroyPool' variable is a global variable, defined in the same menu.js file:
var destroyPool = new Ext.DestroyPool();
And the Ext.DestroyPool 'class' is defined as:
Ext.DestroyPool = function(){
this.list = new Array();
}
Ext.DestroyPool.prototype = {
add : function(component, args){
if(this.contains(component))
throw 'Component already registered';
if(args != null && args.length > 2)
throw 'No more than 2 arguments supported';
this.list[this.list.length] = new Array(component,args);
},
destroy : function(){
for(var i=0; i<this.list.length; i++) {
if(this.list[i]!==null) {
var def = this.list[i];
var component = def[0];
if(def[1] == null) {
component.destroy();
} else {
var args = def[1];
if (args.length == 1) {
component.destroy(args[0]);
} else if (args.length == 2) {
component.destroy(args[0], args[1]);
}
}
}
}
this.list = new Array();
},
contains : function(handler) {
for(var i=0; i<this.list.length; i++) {
if(handler === this.list[i][0]) {
return true;
}
}
return false;
}
}
So, what we have here is a utility class that is able to keep track of components that need to be destroyed before loading a new page, and that is also able to actually call the destroy methods of those components.
But these components need to be registered, so after rendering the grid, I 'register' the grid and a toolbar header I user above the grid to display a search field:
var grid = new Ext.grid.Grid('grid', {
....
});
grid.render();
...
var gridHeaderToolbar = new Ext.Toolbar(grid.getView().getHeaderPanel(true));
....
destroyPool.add(gridHeaderToolbar); // call gridHeaderToolbar.destroy()
destroyPool.add(grid, new Array(false, true)); // call grid.destroy(false, true)
I noticed that destroying the paging toolbar of the grid causes some errors, so I assume that the paging toolbar is destroyed by the grid (Is that true?)
I've played with the app for a while and the processmonitor showed no increase in memory usage any more.
I'm pretty sure there's another way, like listening to changes in the content of the content div, but this is pretty straightforward to me, so it should be for anyone else ;)
Anyway, as always, any comments/improvements/remarks are welcome.
Greetz from Holland
Ronaldo
While learning ext, I read some posts about the fact that you, that is, the developer, needs to make sure that components are destroyed properly if you do not need a component any more. Otherwise, the browser will consume more and more memory.
As I intented to use a single index page with the menu and the extjs lib etc, and then reload partial pages with the requested content, I needed a general way to dispose of the components being displayed before refreshing the page.
I though I might share it as well. This is what I've come up with:
The index page has the page layout, a content div (in which the requested pages need to be loaded) and a menu.js with a mainmenu definition:
Ext.onReady(function(){
var tb = new Ext.Toolbar('mainmenu');
tb.add(
new Ext.Toolbar.Button({
text: 'Users',
handler: loadContent('/HDD/crud/browseUsers.do')
}),
new Ext.Toolbar.Button({
text: 'Companies',
handler: loadContent('/HDD/crud/browseCompanies.do')
})
);
});
Notice that the handler is defined as a global function in the same menu.js file:
function loadContent(url) {
return function(){
Ext.get("content").load({
url: url,
scripts:true,
text: "Loading..."
});
}
}
Now as a user clicks the Companies button, a jsp page with a grid div is loaded, and a grid is created etc etc. Works fine. But on switching to another page, like the Users page, the old Company grid remains in memory. So before switching to another page, in the loadContent function, I call destroyPool.destroy().
function loadContent(url) {
return function(){
destroyPool.destroy();
Ext.get("content").load({
url: url,
scripts:true,
text: "Loading..."
});
}
}
The 'destroyPool' variable is a global variable, defined in the same menu.js file:
var destroyPool = new Ext.DestroyPool();
And the Ext.DestroyPool 'class' is defined as:
Ext.DestroyPool = function(){
this.list = new Array();
}
Ext.DestroyPool.prototype = {
add : function(component, args){
if(this.contains(component))
throw 'Component already registered';
if(args != null && args.length > 2)
throw 'No more than 2 arguments supported';
this.list[this.list.length] = new Array(component,args);
},
destroy : function(){
for(var i=0; i<this.list.length; i++) {
if(this.list[i]!==null) {
var def = this.list[i];
var component = def[0];
if(def[1] == null) {
component.destroy();
} else {
var args = def[1];
if (args.length == 1) {
component.destroy(args[0]);
} else if (args.length == 2) {
component.destroy(args[0], args[1]);
}
}
}
}
this.list = new Array();
},
contains : function(handler) {
for(var i=0; i<this.list.length; i++) {
if(handler === this.list[i][0]) {
return true;
}
}
return false;
}
}
So, what we have here is a utility class that is able to keep track of components that need to be destroyed before loading a new page, and that is also able to actually call the destroy methods of those components.
But these components need to be registered, so after rendering the grid, I 'register' the grid and a toolbar header I user above the grid to display a search field:
var grid = new Ext.grid.Grid('grid', {
....
});
grid.render();
...
var gridHeaderToolbar = new Ext.Toolbar(grid.getView().getHeaderPanel(true));
....
destroyPool.add(gridHeaderToolbar); // call gridHeaderToolbar.destroy()
destroyPool.add(grid, new Array(false, true)); // call grid.destroy(false, true)
I noticed that destroying the paging toolbar of the grid causes some errors, so I assume that the paging toolbar is destroyed by the grid (Is that true?)
I've played with the app for a while and the processmonitor showed no increase in memory usage any more.
I'm pretty sure there's another way, like listening to changes in the content of the content div, but this is pretty straightforward to me, so it should be for anyone else ;)
Anyway, as always, any comments/improvements/remarks are welcome.
Greetz from Holland
Ronaldo