| Summary: Ext 클래스를 확장하는 데 필요한 단계를 살펴봅니다. |
| Author: Jozef Sakalos (번역:윤재홍) |
| Published: September 3, 2007 |
| Ext Version: 1.1+ / 2.0+ |
Languages: English Chinese Korean
|
Contents |
텍스트 앞에 아이콘을 보여주는 Ext.form.Combobox 확장 클래스를 만들어봅시다. 예를 들자면, 이런 콤보박스는 국가 선택을 할 때 나라 이름 앞에 국기를 보여주는 경우에 유용할 겁니다.
우리 확장 클래스에게 Ext.ux.IconCombo라는 이름을 지어줍시다.
첫번째 단계는 개발 과정에 필요할 파일을 준비하는 겁니다. 아마 이런 파일들을 필요로 할겁니다.
<!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>
이 HTML은 Application Layout for Beginners 튜토리얼에서 소개된 부분에서 가져와서 조금 수정했습니다.
/** * 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
이 파일에서는 갖고 놀면서 우리의 확장 클래스를 테스트하기 위한 하나의 IconCombo 클래스를 하나 생성했습니다.
// 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
이 파일에서 우리가 한 것이라고는 단지 Ext.form.ComboBox의 기능만을 갖고 있는 껍데기 확장 클래스를 만든 것 뿐입니다. 하지만, 지금 단계에서는 단지 작업할 수 있는 시작점을 필요로 하기에 이걸로 충분합니다.
.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; }
여러분의 경로는 국기 아이콘들을 어디에 저장했는지에 따라 다를겁니다. 국기 아이콘들은 famfamfam.com에서 다운로드 받으시면 됩니다.
현재까지 아주 좋습니다. 인터넷 브라우져로 iconcombo.html을 열어보면, 여러분은 아마 한개의 일반적인 콤보박스를 보게 될 겁니다. 이 콤보박스는 세개의 아이템을 갖고 있고 독일이 선택되어 있을겁니다. 맞지요? 물론, 아직 아이콘은 없지요...
자 이제 작업을 시작해볼까요. 다음 코드를 Ext.ux.IconCombo.js 파일에 추가하시는데, 부모 생성자 호출부분 바로 다음에 추가하세요.
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>' ;
여기서는 기본 콤보박스 아이템 템플릿을 iconClsField를 이용하는 우리의 템플릿으로 오버라이드 했습니다.
이제는 다음 내용을 Ext.ux.IconCombo.css 파일에 추가하세요.
.x-icon-combo-icon { background-repeat: no-repeat; background-position: 0 50%; width: 18px; height: 14px; }
좋군요. 다음 테스트를 위한 준비가 되었으니 브라우져에 있는 페이지를 다시 불러보시죠. 멋지지 않습니까?
리스트가 펼쳐졌을 때 멋진 아이콘들을 갖게 되었는데, 이게 접혔을 때도 국기가 보이게 하고 싶군요. 여러분도 그렇지요?
그럼 다음 코드를 생성자에 있는 템플릿 생성 부분 뒤에 넣어보세요.
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' }); }} });
이 코드는 render 이벤트 리스너를 추가하는 겁니다. 이 리스너는 엘리멘트들에 스타일을 적용하고 국기를 위한 div 콘테이너를 추가합니다.
그리고, 다음 내용을 extend 부분에 추가합니다. 그럼 이렇게 되겠지요.
// 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
우리는 setIconCls 함수를 추가하고 setValue 함수를 오버라이드했습니다. 당연히, 우리는 원래의 setValue 함수가 제 역할을 하기를 원하기 때문에, 먼저 부모의 setValue 함수를 호출하고 나서 우리가 만든 setIconCls 함수를 호출합니다.
다음 내용을 여러분의 Ext.ux.IconCombo.css 파일에 추가하세요.
.x-icon-combo-input { padding-left: 26px; } .x-form-field-wrap .x-icon-combo-icon { top: 3px; left: 6px; }
자 이제 마지막 테스트입니다. HTML 페이지를 다시 불러보세요. 갖다 붙이는 데 실수만 하지 않았다면, 여러분은 이제 자신만의 새로운 Ext.ux.IconCombo 확장 클래스를 갖게 된 겁니다. 여러분이 여기까지 한 내용은 Ext 클래스를 확장하는 가장 기본적인 것들이었고, 여러분은 더 개선할 수 있을 것입니다.
Brian Moeskau 덕분에, 저는 Ext.ux.IconCombo를 간소화시켰고, 이제는 여러분의 어플리케이션에 사용될 수 있을 정도의 최종 버전이 되었다고 생각됩니다. 그 자바스크립트 코드와 css는 다음과 같습니다.
// 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 || '<div class="x-combo-list-item x-icon-combo-item {' + this.iconClsField + '}">{' + this.displayField + '}</div>' ; 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
/* 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; }