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:

Border layout

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:

Container layout

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
    }
  ]
});

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:

Fill Container with scroll bar

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:

Fill Container with stretched item

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;

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.

4 Responses to Ext.ux.layout.FillContainer

  1. Jorge says:

    Interesting approach. Thanks for sharing.

  2. Animal says:

    I like the concept Ed. Couldn’t VBoxLayout be modified to process a minHeight config of flexed items?

  3. Ed Spencer says:

    I suspect it could – let’s see what falls out of the current refactoring as there were some issues with the Box layouts and overflows which were causing us some problems…

  4. nick tulip says:

    very nice and simple. thank you.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

%d bloggers like this: