Menus, Toolbars, and Buttons in Ext JS 3.2

Learning Ext JS 3.2

, , ,
October 2010

$10.00

Build dynamic, desktop-style user interfaces for your data-driven web applications using Ext JS

 

Learning Ext JS 3.2

Learning Ext JS 3.2

Build dynamic, desktop-style user interfaces for your data-driven web applications using Ext JS

  • Learn to build consistent, attractive web interfaces with the framework components
  • Integrate your existing data and web services with Ext JS data support
  • Enhance your JavaScript skills by using Ext's DOM and AJAX helpers
  • Extend Ext JS through custom components
  • An interactive tutorial packed with loads of example code and illustrative screenshots
        Read more about this book      

(For more resources on Ext JS, see here.)

What's on the menu?

We will begin by introducing the Menu class which will be used in all following examples.

We are going to demonstrate usage of the Menu class as both a static component within a page, and as a popup. Both menus will be configured with the same options by using a technique where we define a variable called menuItems to reference an array which specifies the menu's items, and use it in both cases.

The Menu class inherits from Container, so any menu options are child Components specified in the items config. It also inherits the usual Component config options such as renderTo, and the width option.

The static menu will be rendered to the document body, and in order for it to be rendered as a visible, static element in the document, we configure it with floating: false.

So the configuration we end up with is as follows:

new Ext.menu.Menu({
renderTo: document.body,
width: 150,
floating: false,
items: menuItems
});

The popup menu needs no extra configuring aside from its items. We do need to decide when and where to display it. In this case we will add a contextmenu (right click) event listener to the document, and show the menu at the mouse event's position:

var contextMenu = new Ext.menu.Menu({
items: menuItems
});
Ext.getDoc().on({
contextmenu: function(eventObj) {
contextMenu.showAt(eventObj.getXY());
},
stopEvent: true
});

When we run this example, the static menu will be visible. When we right click on the document, the result should be the two menus shown below. Notice how only the second, popup menu has a shadow to indicate that it floats above the document.

The menu's items

The menuItems variable references an array which should be familiar by now. Just like the items config of a FormPanel, it's a list of child Components or config objects. In a menu, a config object with no xtype creates a MenuItem Component. The MenuItem class accepts the following config options in addition to those it inherits:

  • icon: The URL of an image to display as an icon
  • iconCls: A CSS class name which allows a stylesheet to specify a background image to use as an icon
  • text: The text to display
  • handler: A function to call when the item is clicked
  • menu: A Menu object, or Menu configuration object or an array of menu items to display as a submenu when the item is clicked

Because a menu inherits from Container, it can accept other Components as child items. If some complex, menu option dependent input is required, a menu may be configured with a panel as a child item. The menu config of "Menu Option 2" we're creating next contains a FormPanel as its sole child item:

{
text: 'Menu Option 2',
iconCls: 'flag-green',
menu: {
plain: true,
items: {
xtype: 'form',
border: false,
bodyStyle: 'background:transparent;padding:5px',
labelWidth: 70,
width: 300,
defaults: {
anchor: '100%'
},
items: [{
xtype: 'combo',
editable: false,
fieldLabel: 'Select',
triggerAction: 'all',
store: [ [0, 'One or...'], [1 ,'The other']],
value: 0,
getListParent: function() {
return this.el.up('div.x-menu');
}
}, {
xtype: 'textfield',
fieldLabel: 'Title'
}],
fbar: [{
text: 'Submit'
}]
}
}
}

The configurations in the above object will mostly be familiar by now.

There is one extra config we use for the menu which contains the FormPanel.

  • plain: Specify as true so that the menu does not have to show the incised line for separating icons from text

The panel within the menu has the following configs:

border: Specify as false to produce a panel with no borders.

bodyStyle: A CSS style string to apply to the document body. We want to make it transparent to allow the menu to show, and we apply padding.

The ComboBox must render its dropdown list to the menu's element so that clicking on the list does not trigger the menu to hide:

  • GetListParent: This is a function which a ComboBox may be configured with. It must return the HTML element to render the dropdown list into. By default a ComboBox renders its dropdown into the document. We call the up function of the Ext.Element class to find the ancestor node of the combo's element which is a DIV which has the CSS class "x-menu".

The FormPanel as a child of a menu will display like this:

A toolbar for every occasion

An Ext JS Panel, and every Ext JS Component which inherits from the Panel class (This includes Window, TreePanel, and GridPanel) can be configured to render and manage a toolbar docked above, or below the panel's body—or both if really necessary. These are referred to as the top and bottom toolbars, or tbar and bbar for short.

Panels and subclasses thereof may also be configured with a footer bar which renders buttons right at the bottom of the panel—below any bottom toolbar.

The Toolbar class is also an Ext JS Component in its own way, and may when necessary be used on its own, or as a child Component of any Container.

Our second example renders a toolbar standalone into the body of the document. We will use all the main button types to illustrate their usage before moving on to add handlers to react to user interaction. The toolbar will contain the following child components:

  • A basic button
  • A button configured with a menu which is displayed when the button is clicked
  • A SplitButton which will display a menu only when its arrow glyph is clicked
  • A CycleButton which on click, cycles between three different options
  • A pair of mutually exclusive toggle buttons of which only one may be in a "pressed" state at once

Ext.onReady(function(){
new Ext.Toolbar({
renderTo: Ext.getBody(),
items: [{
xtype: 'button',
text: 'Button'
},{
xtype: 'button',
text: 'Menu Button',
menu: [{
text: 'Better'
},{
text: 'Good'
},{
text: 'Best'
}]
},{
xtype: 'splitbutton',
text: 'Split Button',
menu: [{
text: 'Item One'
},{
text: 'Item Two'
},{
text: 'Item Three'
}]
}, {
xtype: 'cycle',
showText: true,
minWidth: 100,
prependText: 'Quality: ',
items: [{
text: 'High',
checked: true
}, {
text: 'Medium'
}, {
text: 'Low'
}]
}, {
text: 'Horizontal',
toggleGroup: 'orientation-selector'
}, {
text: 'Vertical',
toggleGroup: 'orientation-selector'
}]
});
});

As usual, everything is inside our onReady event handler. The items config holds our toolbar's entire child Components—I say child Components and not buttons because as we now know, the toolbar can accept many different types of Ext JS Components including entire forms or just form fields—which we will be implementing later on in this article.

The result of the above code looks like this:

The default xtype for each element in the items config is button. We can leave out the xtype config element if button is the type we want, but I like to include it just for clarity.

Button configuration

In addition to inherited config options, a button accepts the following configurations which we will be using in the following examples for this article:

  • icon: The URL of an image to display as an icon
  • iconCls: A CSS class name which allows a stylesheet to specify a background image to use as an icon
  • text: The text to display
  • handler: A function to call when the button is clicked
  • menu: A Menu object, or Menu configuration object, or an array of menu items to display as a submenu when the button is clicked
  • enableToggle: Specify as true to make a single button toggleable between pressed and unpressed state
  • toggleGroup: A mnemonic string identifying a group of buttons of which only one may be in a "pressed" state at one time
  • toggleHandler: A function to be called when a button's "pressed" state is changed

A basic button

Creating a button is fairly straightforward; the main config option is the text that is displayed on the button. We can also add an icon to be used alongside the text if we want to. A handler function is called when the button is clicked.

Here is the most basic configuration of a button:

{
xtype: 'button',
text: 'Button',
handler: functionReference
}

The following screenshot shows what happens when the mouse is hovered over the Button button:

Button with a menu

A button may be configured to act as a trigger for showing a dropdown menu. If configured with a menu option, clicking the button displays a menu below the button. The alignment of the menu is configurable, but defaults to being shown below the button.

Each option within the menu may itself be configured with a menu option allowing a familiar cascading menu system to be built very easily. The following is a config for a button which displays a dropdown menu upon click:

{
xtype: 'button',
text: 'Button',
menu: [{
text: 'Better'
},{
text: 'Good'
},{
text: 'Best'
}]
}

The following screenshot shows what happens when the Menu Button is clicked on, and the mouse is hovered over the Best option:

        Read more about this book      

(For more resources on Ext JS, see here.)

Split button

A split button may sound like a complex component, but it is no more complex to create than a plain button. By using a split button we get the ability to specify a click handler which is called when the main body of the button is clicked. But we can also configure in a menu which will be displayed when the arrow glyph to the right of the main body is clicked.

{
xtype: 'split',
text: 'Split Button',
menu: [{
text: 'Item One'
},{
text: 'Item Two'
},{
text: 'Item Three'
}]
}

The following screenshot shows what happens when the Split Button's arrow glyph is clicked:

Toggling button state

Sometimes it is useful to have a button which "sticks" in the pressed state to indicate switching some state within our app. To enable a single button to be clicked to toggle between the pressed and unpressed state, configure the button with enableToggle: true.

If a set of buttons are to toggleable, but only one may be pressed at once, configure each button in that set with a toggleGroup. This is an ID string which links buttons which enforce this rule.

Toggleable buttons may be configured with a toggleHandler function which is called whenever the button's state changes in either direction.

{
text: 'Horizontal',
toggleGroup: 'orientation-selector'
}, {
text: 'Vertical',
toggleGroup: 'orientation-selector'
}

This code produces the pair of buttons below in which only one orientation may be selected at once, Horizontal or Vertical:

Toolbar item alignment, dividers, and spacers

By default, every toolbar aligns elements to the leftmost side. There is no alignment config for a toolbar, so if we want to align all of the toolbar buttons to the rightmost side, we need to add a fill item to the toolbar. This item is sometimes referred to as a 'greedy spacer'. If we want to have items split up between both the left and right sides, we can also use a fill, but place it between items:

{
xtype: 'tbfill'
}

Pop this little guy in a toolbar wherever you want to add space and it will push items on either side of the fill to the ends of the tool bar, as shown below:

We also have elements that can add space or a visual vertical divider, like the one used between the Menu Button and the Split Button.

The spacer adds a few pixels of empty space that can be used to space out buttons, or move elements away from the edge of the toolbar:

{
xtype: 'tbspacer'
}

A divider can be added in the same way:

{
xtype: 'tbseparator'
}

Shortcuts

Ext JS has many shortcuts that can be used to make coding faster. Shortcuts are a character or two that can be used in place of a configuration object. For example, consider the standard toolbar filler configuration:

{
xtype: 'tbfill'
}

The shortcut for a toolbar filler is a hyphen and a greater than symbol:

'->'

Not all of these shortcuts are documented. So be adventurous, poke around the source code that is distributed in the src folder of the SDK download, and see what you can find. Here is a list of the commonly-used shortcuts:

Icon buttons

The standard button can be used as an icon button like the ones we see used in text editors to make text bold or italic—simply a button with an icon and no text. Two steps need to be taken to make an icon button—defining an image to be used as the icon and applying the appropriate class to the button.

{
xtype: 'tbbutton',
cls: 'x-btn-icon',
icon: 'images/bomb.png'
}

This could just as easily be an icon beside text by changing the style class and adding the text config.

{
xtype: 'tbbutton',
cls: 'x-btn-text-icon',
icon: 'images/bomb.png',
text: 'Tha Bomb'
}

Another method for creating an icon button is to apply a CSS class to it that contains a background image. With this method we can keep the references to images in our CSS instead of our JavaScript, which is preferable whenever possible.

{
xtype: 'tbbutton',
iconCls: 'bomb'
}

The CSS to go along with this would have a background image defined, and the CSS 'important' flag set.

.bomb {
background-image: url( images/bomb.png ) !important;
}

Button events and handlers—click me!

A button needs to do more than just look pretty—it needs to react to the user. This is where the button's handler and events come in. A handler is a function that is executed when a button is clicked.

The handler config is where we add our function, which can be an anonymous function like below or a method of a class—any function will do:

{
xtype: 'button',
text: 'Button',
handler: function(){
Ext.Msg.alert('Boo', 'Here I am');
}
}

This code will pop up an alert message when the button is clicked. Just about every handler or event in Ext JS passes a reference to the component that triggered the event as the first argument. This makes it easy to work with whichever component that fired this handler, calling the disable method on it.

{
xtype: 'tbbutton',
text: 'Button',
handler: function(f){
f.disable();
}
}

We can take this reference to the button—a reference to itself—and access all of the properties and functions of that button. For this sample, we have called the disable function which grays out the button and makes it non-functional.

We can have more fun than just disabling a button. Why don't we try something more useful?

        Read more about this book      

(For more resources on Ext JS, see here.)

Loading content on menu item click

Let's take our button click and do something more useful with it. For this example, we are going to add a config option named helpfile to each menu item that will be used to determine what content file to load in the body of our page:

{
xtype: 'tbsplit',
text: 'Help',
menu: [{
text: 'Genre',
helpfile: 'genre',
handler: Movies.showHelp
},{
text: 'Director',
helpfile: 'director',
handler: Movies.showHelp
},{
text: 'Title',
helpfile: 'title',
handler: Movies.showHelp
}]
}

Note the helpfile config option that we have added to each of the menu items configs. We have made this config property up so that we have a way to store a variable that is unique to each menu item. This is possible because config properties can be anything we need them to be, and are copied into the Component upon construction, and become properties of the Component. In this case, we are using a config property as a reference to the name of the file we want to load.

The other new thing we are doing is creating a collection of functions to handle the menu item click. These functions are all organized into a Movies object.

var Movies = function() {
var helpbody;
return {
showHelp : function(btn){
Movies.doLoad(btn.helpfile);
},
doLoad : function(file){
helpbody = helpbody ||
Ext.getBody().createChild({tag:'div'});
helpbody.load({
url: 'html/' + file + '.txt'
});
},
setQuality: function(q) {
helpbody = helpbody ||
Ext.getBody().createChild({tag:'div'});
helpbody.update(q);
}
};
}();

This piece of code is worth reading a few times until you understand it. It illustrates the creation of a singleton object.

Notice that the function has () after it. It is called immediately returning an object, instead of a class that could be instantiated.

The Movies variable is assigned to reference whatever that function returns. It does not reference the function.

The object returned from that function contains three functions. These are the "member functions" of the Movies singleton object. It offers utility methods which our example code will use.

All the functions have access to the helpbody variable because they were declared within the same function it was declared in. They can use that variable, but it is not available to outside code. This is an important capability of singleton objects built in this slightly confusing manner.

Form fields in a toolbar

As the Toolbar class inherits from Container, it can be used to house any Ext JS Component. Naturally, form fields and combo boxes are very useful items to have on a toolbar. These are added as child items just as with all Container classes we have encountered so far:

{
xtype: 'textfield'
}

In the same way as we created form fields, we add the form fields to the items array, which will place the form fields within the toolbar. Now let's make the form field do something useful, by having it perform the same functionality as our help menu, but in a more dynamic way.

{
xtype: 'textfield',
listeners: {
specialkey: Movies.doSearch
}
}

This listener is added directly to the form field's config. For this, we are using a specialkey listener. This is the listener that is used to capture edit keystrokes, such as Enter and Delete among others. The handler function will be added to our small Movies class created earlier:

doSearch : function(field, keyEvent){
if (keyEvent.getKey() == Ext.EventObject.ENTER) {
Movies.doLoad(field.getValue());
}
}

Now the text field in our toolbar that enables us to type in the name of the text file to load should look like the following. Try typing in some of the samples used in our menu, such as director or title:

Buttons don't have to be in a toolbar

Up until now, we have been dealing with buttons in toolbars. But because buttons are part of the Ext JS Components class hierarchy, they can be used as child items of any Container, not just toolbars. When used outside of a toolbar, they are themed in a slightly different way.

A button can be added to the items array, just like we would add a panel or other child components.

new Ext.Window({
title: 'Help',
id: 'helpwin',
width: 300,
height: 300,
items: [{
xtype: 'button',
text: 'Close',
handler: function(){
Ext.getCmp('helpwin').close();
}
}]
}).load("html/director.txt ").show();

The above code fragment would produce the following display:

Toolbars in panels

Toolbars can be configured into all Ext JS Panel classes (Panel, GridPanel, TreePanel, and Window) docked either above or below the panel, body (or both)

Panel classes, such as the window and the grid, have a top and bottom toolbar config, along with a buttons config:

  • tbar: A toolbar, or toolbar configuration object, or an array specifying toolbar child items. This causes a toolbar to be docked above the panel's body.
  • bbar: A toolbar, or toolbar configuration object, or an array specifying toolbar child items. This causes a toolbar to be docked below the panel's body.
  • fbar: Also may be specified as buttons. A toolbar, or toolbar configuration object, or an array specifying toolbar child items, which specifies a toolbar to be placed into the panel's footer element.

If we wanted to place a toolbar at the top of a window, we would specify a tbar config option which references an array of toolbar child config objects like this:

new Ext.Window({
title: 'Help',
width: 300,
height: 300,
renderTo: document.body,
closeAction: 'hide',
layout: 'fit',
tbar: [{
text: 'Close',
handler: function(){
winhelp.hide();
}
},{
text: 'Disable',
handler: function(t){
t.disable();
}
}]
});

When this window is activated by clicking the Director help menu entry, this produces the following display:

Of course if we wanted that same toolbar on the bottom of the window, we can change from a tbar to a bbar.

We can also specify a toolbar (or config of a toolbar, or toolbar's items) to be rendered into the footer area of a panel or window. Buttons are themed as normal buttons in a footer bar, and by default are aligned to the right:

new Ext.Window({
title: 'Help',
width: 300,
height: 300,
renderTo: document.body,
closeAction: 'hide',
layout: 'fit',
fbar: [{
text: 'Close',
handler: function(){
winhelp.hide();
}
}]
});

If the help window was configured in this way, it would display like this:

Ext JS also has a custom toolbar for paged grids which contains all of the buttons for moving through pages of results.

Toolbars unleashed

As mentioned above, the toolbar class is a special type of Container class which lays out its child components in a particular, preconfigured way. We can use it to house more complex Components, and we can experiment with using different layout types to size and arrange the child Components in different ways.

In the final example, we use the hbox (horizontal box) layout to arrange ButtonGroups across the toolbar, and force them to be vertically sized to the size of the highest one, to produce a pleasing, even ribbon effect.

new Ext.Panel({
title: 'Panel with heavyweight toolbar',
renderTo: document.body,
width: 600,
height: 400,
tbar: new Ext.Toolbar({
layout: {
type: 'hbox',
align: 'stretchmax'
},
items: [{
xtype: 'buttongroup',
title: 'Group 1',
columns: 1,
items: [{
text: 'Group 1 Button One',
handler: handlerFn
},{
text: 'Group 1 Button Two',
handler: handlerFn
},{
text: 'Group 1 Button Three',
handler: handlerFn
}]
},{

This example will produce output like this:

This example illustrates the concept that a toolbar is a Container which may contain any Component, and also that a ButtonGroup is a Container with the same heritage. The first three ButtonGroups contain Buttons; the last one for a slight change of style contains MenuItems.

Summary

In this article, we had the chance to play with a couple of different ways to create toolbar items, including using a config object or its shortcut. The many options available for toolbars make them a useful component for everything from the simplest button bar, to a complex combination of buttons, menus, and form fields. Interacting with the buttons, menus, and form fields is easy using the built-in handlers.


Further resources on this subject:


Books to Consider

comments powered by Disqus