| 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: English Chinese Korean
|
Contents |
ObjectiveLet'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 FilesFirst step is to prepare files we will need in the process of development. We will need these files:
iconcombo.html<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <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 goSo 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 finaleNow 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; }