Ext JS - Learning Center

Tutorial:Extending Ext Class

From Learn About the Ext JavaScript Library

Jump to: navigation, search
Summary: This tutorial will walk you through steps necessary to extend an Ext class.
Author: Jozef Sakalos
Published: September 3, 2007
Ext Version: 1.1+ / 2.0+
Languages: en.png English cn.png Chinese kr.png Korean

br.png Portuguese

Contents

Forum Thread

For questions about this tutorial please consult and use this thread.

Objective

Intended resulting IconCombo

Let's create an extension of Ext.form.Combobox that will display icons in front of texts. Such combo could be useful, for example, for selection of countries when we'd have the country flag followed by the country name.

Let's give our extension name Ext.ux.IconCombo.

Create Files

First step is to prepare files we will need in the process of development. We will need these files:

  • iconcombo.html: html markup for application that will use our new extension
  • iconcombo.js: the application javascript code
  • Ext.ux.IconCombo.js: javascript file of our extension
  • Ext.ux.IconCombo.css: style sheets for our extension

iconcombo.html

<html>
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
    <link rel="stylesheet" type="text/css" href="../extjs/resources/css/ext-all.css">
    <link rel="stylesheet" type="text/css" href="Ext.ux.IconCombo.css">
    <script type="text/javascript" src="../extjs/adapter/ext/ext-base.js"></script>
    <script type="text/javascript" src="../extjs/ext-all-debug.js"></script>
    <script type="text/javascript" src="Ext.ux.IconCombo.js"></script>
    <script type="text/javascript" src="iconcombo.js"></script>
    <!-- A Localization Script File comes here -->
    <script type="text/javascript">Ext.onReady(iconcombo.init, iconcombo);</script>
    <title>Ext.ux.IconCombo Tutorial</title>
</head>
<body>
<div style="position:relative;width:300px;top:24px;left:64px;font-size:11px">
    <div>Icon combo:</div>
    <div id="combo-ct"></div>
</div>
</body>
</html>

This is only slightly modified file from Application Layout for Beginners Tutorial.

iconcombo.js

/**
  * Ext.ux.IconCombo Tutorial
  * by Jozef Sakalos, aka Saki
  * http://extjs.com/learn/Tutorial:Extending_Ext_Class
  */
 
// reference local blank image
Ext.BLANK_IMAGE_URL = '../extjs/resources/images/default/s.gif';
 
// create application
iconcombo = function() {
 
    // public space
    return {
        // public properties, e.g. strings to translate
 
        // public methods
        init: function() {
            var icnCombo = new Ext.ux.IconCombo({
                store: new Ext.data.SimpleStore({
                    fields: ['countryCode', 'countryName', 'countryFlag'],
                    data: [
                        ['US', 'United States', 'x-flag-us'],
                        ['DE', 'Germany', 'x-flag-de'],
                        ['FR', 'France', 'x-flag-fr']
                    ]
                }),
                valueField: 'countryCode',
                displayField: 'countryName',
                iconClsField: 'countryFlag',
                triggerAction: 'all',
                mode: 'local',
                width: 160
            });
            icnCombo.render('combo-ct');
            icnCombo.setValue('DE');
        }
    };
}(); // end of app
 
// end of file

In this file we create one IconCombo so we have something to play with and test our extension on.

Ext.ux.IconCombo.js

// Create user extensions namespace (Ext.ux)
Ext.namespace('Ext.ux');
 
/**
  * Ext.ux.IconCombo Extension Class
  *
  * @author Jozef Sakalos, aka Saki
  * @version 1.0
  *
  * @class Ext.ux.IconCombo
  * @extends Ext.form.ComboBox
  * @constructor
  * @param {Object} config Configuration options
  */
Ext.ux.IconCombo = function(config) {
 
    // call parent constructor
    Ext.ux.IconCombo.superclass.constructor.call(this, config);
 
}; // end of Ext.ux.IconCombo constructor
 
// extend
Ext.extend(Ext.ux.IconCombo, Ext.form.ComboBox, {
 
}); // end of extend
 
// end of file

What we have actually done in this file is an empty extension which does nothing else than Ext.form.ComboBox does. It's fine for now as we just need a workable starting point at this step.

Ext.ux.IconCombo.css

.x-flag-us {
    background-image: url(../img/flags/us.png) !important;
}
.x-flag-de {
    background-image: url(../img/flags/de.png) !important;
}
.x-flag-fr {
    background-image: url(../img/flags/fr.png) !important;
}

Your paths may vary depending on where you put your flag icons that you can download form famfamfam.com.

Let's go

So far so good. If you now navigate to iconcombo.html you should see one standard combo with three items and Germany should be selected, right? No icons yet, of course...


Now it's time to work. Add the following lines to Ext.ux.IconCombo.js just after the call to the parent constructor (note that the tag is the lowercase of TPL, for "template", not TP1):

this.tpl = config.tpl ||
      '<tpl for="."><div class="x-combo-list-item">'
    + '<table><tbody><tr>'
    + '<td>'
    + '<div class="{' + this.iconClsField + '} x-icon-combo-icon"></div></td>'
    + '<td>{' + this.displayField + '}</td>'
    + '</tr></tbody></table>'
    + '</div></tpl>'
;

What we do here is that we override default combo box item template with our own that makes use of the iconClsField.

Now append the following lines to Ext.ux.IconCombo.css:

.x-icon-combo-icon {
    background-repeat: no-repeat;
    background-position: 0 50%;
    width: 18px;
    height: 14px;
}

Good! Ready for next test, so reload the page. Nice yeah?

Well, we have nice icons when the list is open but we'd like to have also flag when it's closed, don't we?

Add following lines after our template creation in constructor:

this.on({
    render:{scope:this, fn:function() {
        var wrap = this.el.up('div.x-form-field-wrap');
        this.wrap.applyStyles({position:'relative'});
        this.el.addClass('x-icon-combo-input');
        this.flag = Ext.DomHelper.append(wrap, {
            tag: 'div', style:'position:absolute'
        });
    }}
});

This code adds render event listener that adjusts styles of elements and adds div container for flag.

Then add the following to the extend part so that it reads:

// extend
Ext.extend(Ext.ux.IconCombo, Ext.form.ComboBox, {
 
    setIconCls: function() {
        var rec = this.store.query(this.valueField, this.getValue()).itemAt(0);
        if(rec) {
            this.flag.className = 'x-icon-combo-icon ' + rec.get(this.iconClsField);
        }
    },
 
    setValue: function(value) {
        Ext.ux.IconCombo.superclass.setValue.call(this, value);
        this.setIconCls();
    }
 
}); // end of extend

We're adding a function setIconCls and overriding the setValue function. Of course, we want the original setValue to do its job so we call it first in our scope and then we call our setIconCls function.

Last but not least, append the following lines to your Ext.ux.IconCombo.css file:

.x-icon-combo-input {
    padding-left: 26px;
}
.x-form-field-wrap .x-icon-combo-icon {
    top: 3px;
    left: 6px;
}

The grand finale

Now the final test: reload the page. If you (or me copying/pasting) haven't made a mistake, you have your new Ext.ux.IconCombo extension. Sure, you can further improve it but these are the basics of extending an Ext class.

Thanks to Brian Moeskau I have simplified Ext.ux.IconCombo so it can be now considered the final version that can be used in your applications. The final code and css follow:

Ext.ux.IconCombo.js

// Create user extensions namespace (Ext.ux)
Ext.namespace('Ext.ux');
 
/**
  * Ext.ux.IconCombo Extension Class
  *
  * @author  Jozef Sakalos
  * @version 1.0
  *
  * @class Ext.ux.IconCombo
  * @extends Ext.form.ComboBox
  * @constructor
  * @param {Object} config Configuration options
  */
Ext.ux.IconCombo = function(config) {
 
    // call parent constructor
    Ext.ux.IconCombo.superclass.constructor.call(this, config);
 
    this.tpl = config.tpl ||
          '<tpl for="."><div class="x-combo-list-item x-icon-combo-item {' 
        + this.iconClsField 
        + '}">{' 
        + this.displayField 
        + '}</div></tpl>'
    ;
 
    this.on({
        render:{scope:this, fn:function() {
            var wrap = this.el.up('div.x-form-field-wrap');
            this.wrap.applyStyles({position:'relative'});
            this.el.addClass('x-icon-combo-input');
            this.flag = Ext.DomHelper.append(wrap, {
                tag: 'div', style:'position:absolute'
            });
        }}
    });
} // end of Ext.ux.IconCombo constructor
 
// extend
Ext.extend(Ext.ux.IconCombo, Ext.form.ComboBox, {
 
    setIconCls: function() {
        var rec = this.store.query(this.valueField, this.getValue()).itemAt(0);
        if(rec) {
            this.flag.className = 'x-icon-combo-icon ' + rec.get(this.iconClsField);
        }
    },
 
    setValue: function(value) {
        Ext.ux.IconCombo.superclass.setValue.call(this, value);
        this.setIconCls();
    }
 
}); // end of extend
 
// end of file

Ext.ux.IconCombo.css

/* application specific styles */
.x-flag-us {
    background-image:url(../img/flags/us.png) !important;
}
.x-flag-de {
    background-image:url(../img/flags/de.png) !important;
}
.x-flag-fr {
    background-image:url(../img/flags/fr.png) !important;
}
 
/* Ext.ux.IconCombo mandatory styles */
.x-icon-combo-icon {
    background-repeat: no-repeat;
    background-position: 0 50%;
    width: 18px;
    height: 14px;
}
.x-icon-combo-input {
    padding-left: 25px;
}
.x-form-field-wrap .x-icon-combo-icon {
    top: 3px;
    left: 5px;
}
.x-icon-combo-item {
    background-repeat: no-repeat !important;
    background-position: 3px 50% !important;
    padding-left: 24px;
}
  • This page was last modified on 6 February 2009, at 18:40.
  • This page has been accessed 82,276 times.