Ext.ux.layout.FillContainer

One of the pages on the Ext JS app I'm currently working on has a form with a grid underneath. The page exists as a tab inside an Ext.TabPanel, and uses the border layout, with the form as the 'north' component and the grid as 'center'.

The trouble with this is that the grid shrinks down to an unusable size when the browser window is too small, ending up like this:

We could alternatively use a basic container layout, but this limits us to a fixed height for the grid, meaning we waste space at the bottom:

Enter the imaginatively named FillContainer:

new Ext.Panel({
autoScroll: true,
layout: 'fillcontainer',
items : [
{
html : 'Pretend this is a form',
height: 400
},
{
html : 'And this is the grid',
minHeight : 250,
fillContainer: true
}
]
});
new Ext.Panel({
autoScroll: true,
layout: 'fillcontainer',
items : [
{
html : 'Pretend this is a form',
height: 400
},
{
html : 'And this is the grid',
minHeight : 250,
fillContainer: true
}
]
});

If our containing panel shrinks to less than 650px in height, the grid will be automatically sized to 250px and a vertical scrollbar will appear on the panel, like this:

If the panel's height increases to, say, 900px, the grid gets resized to 500px high. This way we use the space when it's available, while maintaining a usable interface when height is limited:

Here's the code that makes it work:

Ext.ns('Ext.ux.layout');

/**
* @class Ext.ux.layout.FillContainerLayout
* @extends Ext.layout.ContainerLayout
* @author Ed Spencer (http://edspencer.net)
* Extended version of container layout which expands a given child item to the
* full height of the container, honouring the item's minHeight property
*/
Ext.ux.layout.FillContainerLayout = Ext.extend(Ext.layout.ContainerLayout, {
monitorResize: true,

/**
* After rendering each item, resize the one with fillContainer == true
*/
onLayout: function(ct, target) {
Ext.ux.layout.FillContainerLayout.superclass.onLayout.apply(this, arguments);

var ctHeight = ct.getHeight(),
itemsHeight = 0,
expandItem;

ct.items.each(function(item) {
if (item.fillContainer === true) {
expandItem = item;
} else {
itemsHeight += item.getHeight();
}
});

//set the expand item's height to fill the container
if (expandItem != undefined && ctHeight > itemsHeight) {
var newHeight = ctHeight - itemsHeight;

expandItem.setHeight(Math.max(newHeight, expandItem.minHeight));
}
}
});

Ext.Container.LAYOUTS['fillcontainer'] = Ext.ux.layout.FillContainerLayout;
Ext.ns('Ext.ux.layout');

/**
* @class Ext.ux.layout.FillContainerLayout
* @extends Ext.layout.ContainerLayout
* @author Ed Spencer (http://edspencer.net)
* Extended version of container layout which expands a given child item to the
* full height of the container, honouring the item's minHeight property
*/
Ext.ux.layout.FillContainerLayout = Ext.extend(Ext.layout.ContainerLayout, {
monitorResize: true,

/**
* After rendering each item, resize the one with fillContainer == true
*/
onLayout: function(ct, target) {
Ext.ux.layout.FillContainerLayout.superclass.onLayout.apply(this, arguments);

var ctHeight = ct.getHeight(),
itemsHeight = 0,
expandItem;

ct.items.each(function(item) {
if (item.fillContainer === true) {
expandItem = item;
} else {
itemsHeight += item.getHeight();
}
});

//set the expand item's height to fill the container
if (expandItem != undefined && ctHeight > itemsHeight) {
var newHeight = ctHeight - itemsHeight;

expandItem.setHeight(Math.max(newHeight, expandItem.minHeight));
}
}
});

Ext.Container.LAYOUTS['fillcontainer'] = Ext.ux.layout.FillContainerLayout;

As we're just extending the default container layout, your items will be rendered in the order you specify them. The expanding item doesn't have to be the last one - we could equally have set fillContainer and minHeight on the form to expand that instead of the grid.

Share Post:

What to Read Next