使用ExtJs创建新的UI控件(转)
組合或擴展
當創建一個新類,往往要作出這么的一個選擇:要么擁有某個工具類的實例來扮演首要的角色,要么擴展那個類。
使用ExtJs過程中,推薦從最靠近的基類開始擴展,實現所需的功能即可。這是因為Ext提供的自動生存周期引入了自動渲染的機制、自動大小調整和承擔接受來自布局管理器的UI組件布局調控,還有在容器(Container)中自動銷毀的功能。
組織一個新類,它就是ExtJs的類,實現起來是很方便的,這就會導致了Container→Component層次的形成,相比較,新類擁有一個ExtJs類的話,必須從外圍對其渲染和組織。
The Template method Pattern模板方法模式
Ext Js的Component的繼承層次采用Template Method pattern(模板方法模式)來委派給子類,交由子類來負責其特定的行為。
其意義在于,繼承鏈中的每個子類得以在,組件生存周期的某個階段內,“提交(contribute)”額外的邏輯功能。這樣每一個類擁有屬于其自身的行為;其他類加入自己的行為時也不會相互造成影響。
其中一個例子是render(渲染)函數。render函數不能夠被覆蓋,應該是一層一層地在各子類的實現中加入onRender方法,那么render函數就會在執行的時候把各onRender方法訪問調用。每一個onRender方法必須調用其父類的onRender方法繼而再“埋頭處理(contribute)”自己(子類)的邏輯部分。
下面的圖例演示了模板方法onRender的機能過程。
render是由容器的布局管理器(Container’s layout manager)負責調用。該方法的實現不能被“沖掉(overridden)”,它是由Ext基類提供的。this.onRender表示當前子類所寫的實現(如有提供的話)。它會訪問父類的版本、父類的版本又會調用父、父類的版本……最終,每個類完成了其功能,render函數的返回值對生存周期進行控制。
在ExtJS組件(Component)生存周期中,提供了若干有意義的模板方法以實現類特定的邏輯功能。
強調: 當編寫子類時,模板方法應在實例化的過程中調用,屬于生存周期內的一部分的動作,而不應是事件的一部分。事件有可能會由handler掛起,或中止。
以下是Component子類都可享有的模板方法:
- onRender
- afterRender
- onShow
- onHide
- onDisable
- onEnable
- onDestroy
Ext組件類的各層次中的均有其自身的模板方法,我們可以打開來看看,這些都是根據自身不同的需求而作出的設計。
提示: 當調用父類的模板方法時,最簡潔的方法就是使用Function.apply,保證所有的參數都可以接受得到,傳送給那個模板方法:
Ext.ux.Subclass.superclass.onRender.apply(this, arguments);要擴展哪個類
選擇適合的類來擴展不但要考慮基類提供哪些功能,而且對性能方面也要著重考慮。無論有多少個UI控件被渲染或調控,Ext.Panel常常就是被衍生(extend)的對象。
Panel類擁有許多的能力:
- Border(軀干)
- Header(頭部)
- Header工具條
- Footer(底部)
- Footer按鈕
- Top toolbar(頂部工具條)
- Bottom toolbar(底部工具條)
- 承托和管理子組件
如果這些派不上用場,那使用Panel便是資源浪費。
Component(組件類)
如果要求的UI控件不需要其他的細節的控件,也就是,僅僅是封裝某部分的HTML元素的話,那么可取的擴展對象就是Ext.BoxComponent或Ext.Component。如果再縮窄一步,我不需要聽憑父容器提供的大小調控功能,那么使用Ext.Component就可以了。
強調: Component類并不會內省而得知哪一種元素作為holder。因此為了創建所需的元素(Element),應設定autoEl的配置項。
例如,要把一張圖片封裝為Component,我們于是乎這樣定義:
Ext.ux.Image = Ext.extend(Ext.Component, {autoEl: {tag: 'img',src: Ext.BLANK_IMAGE_URL,cls: 'tng-managed-image'},// Add our custom processing to the onRender phase. // We add a ‘load’ listener to our element.onRender: function() {Ext.ux.Image.superclass.onRender.apply(this, arguments);this.el.on('load', this.onLoad, this);},onLoad: function() {this.fireEvent('load', this);},setSrc: function(src) {this.el.dom.src = src;} });這是一個可封裝圖片的Ext Component類,可參與非箱子方寸模型(non box-sizing)的布局。
BoxComponent
如果要求的UI控件不需要其他的細節的控件,也就是,僅僅是封裝某部分的HTML元素的話,還要聽憑布局管理器提供的大小尺寸、布局的調控,那么這個的擴展對象就是Ext.BoxComponent。
例如,假設一個Logger類打算是簡單地顯示log信息,就必須嵌入某種布局的風格,例如插入到一個layout:’fit’窗體,可以這樣定義:
Ext.ux.Logger = Ext.extend(Ext.BoxComponent, {tpl: new Ext.Template("<li class='x-log-entry x-log-{0:lowercase}-entry'>","<div class='x-log-level'>","{0:capitalize}","</div>","<span class='x-log-time'>","{2:date('H:i:s.u')}","</span>","<span class='x-log-message'>","{1}","</span>","</li>"),autoEl: {tag: 'ul',cls: 'x-logger'},onRender: function() {Ext.ux.Logger.superclass.onRender.apply(this, arguments);this.contextMenu = new Ext.menu.Menu({items: [new Ext.menu.CheckItem({id: 'debug',text: 'Debug',checkHandler: Ext.ux.Logger.prototype.onMenuCheck,scope: this}), new Ext.menu.CheckItem({id: 'info',text: 'Info',checkHandler: Ext.ux.Logger.prototype.onMenuCheck,scope: this}), new Ext.menu.CheckItem({id: 'warning',text: 'Warning',checkHandler: Ext.ux.Logger.prototype.onMenuCheck,scope: this}), new Ext.menu.CheckItem({id: 'error',text: 'Error',checkHandler: Ext.ux.Logger.prototype.onMenuCheck,scope: this})]});this.el.on('contextmenu', this.onContextMenu, this, {stopEvent: true});},onContextMenu: function(e) {this.contextMenu.logger = this;this.contextMenu.showAt(e.getXY());},onMenuCheck: function(checkItem, state) {var logger = checkItem.parentMenu.logger;var cls = 'x-log-show-' + checkItem.id;if (state) {logger.el.addClass(cls);} else {logger.el.removeClass(cls);}},debug: function(msg) {this.tpl.insertFirst(this.el, ['debug', msg, new Date()]);this.el.scrollTo("top", 0, true);},info: function(msg) {this.tpl.insertFirst(this.el, ['info', msg, new Date()]);this.el.scrollTo("top", 0, true);},warning: function(msg) {this.tpl.insertFirst(this.el, ['warning', msg, new Date()]);this.el.scrollTo("top", 0, true);},error: function(msg) {this.tpl.insertFirst(this.el, ['error', msg, new Date()]);this.el.scrollTo("top", 0, true);} });接著是CSS:
.x-logger {overflow: auto; } .x-log-entry .x-log-level {float: left;width: 4em;text-align: center;margin-right: 3px; } .x-log-entry .x-log-time {margin-right: 3px; } .x-log-entry .x-log-message {margin-right: 3px; } .x-log-debug-entry, .x-log-info-entry, .x-log-warning-entry, .x-log-error-entry {display: none; }.x-log-show-debug .x-log-debug-entry { display: block } .x-log-show-info .x-log-info-entry { display: block } .x-log-show-warning .x-log-warning-entry { display: block } .x-log-show-error .x-log-error-entry { display: block }.x-log-debug-entry .x-log-level { background-color: #46c } .x-log-info-entry .x-log-level { background-color: green } .x-log-warning-entry .x-log-level { background-color: yellow } .x-log-error-entry .x-log-level { background-color: red }我們吧log的信息的HTML列表均放置在一個布局中。我們在onRender的階段加入處理,使得右鍵菜單可以根據CSS樣式類的名稱操控logged條目的可見性。位于該層次的對象還提供了特別的模板方法:
- onResize
- onPosition
Container(容器類)
如果要求的UI控件將用于承載(Contain)其他UI元素在其身上,但并不需要前文提及到的Ext.Panel那么多的功能,為避免臃腫,應采用Ext.Container容器類來繼承。同樣地,autoEl指定元素的配置項亦必不可少,將用于容器在某個元素之上進行渲染。同樣,在視覺控制方面,滾動條是否顯示方面(即overflow屬性),用戶都可以使用Style配置項,或容器元素的class屬性的兩種方式進行CSS樣式制定。
注意: 對于Container層次,不要忘記哪種布局類是被用于渲染和調控子組件的。
示例中的類封裝了條件命令的查詢,允許用戶對Store基于測試字段的數據篩選。除了功能上的封裝外,還把查詢任務作統一布局,封裝在一個可控類中,可方便從容器身上自動添加或移除查詢的條目,靈活性更高:
Ext.ux.FilterCondition = Ext.extend(Ext.Container, {layout: 'table',layoutConfig: {columns: 7},autoEl: {cls: 'x-filter-condition'},Field: Ext.data.Record.create(['name', 'type']),initComponent: function() {this.fields = this.store.reader.recordType.prototype.fields;this.fieldStore = new Ext.data.Store();// Create a Store containing the field names and types // in the passed Store.this.fields.each(function(f) {this.fieldStore.add(new this.Field(f))}, this);// Create a Combo which allows selection of a fieldthis.fieldCombo = new Ext.form.ComboBox({triggerAction: 'all',store: this.fieldStore,valueField: 'name',displayField: 'name',editable: false,forceSelection: true,mode: 'local',listeners: {select: this.onFieldSelect,scope: this}});// Create a Combo which allows selection of a testthis.testCombo = new Ext.form.ComboBox({triggerAction: 'all',store: ['<', '<=', '=', '!=', '>=', '>']});// Inputs for each type of field. Hidden and shown as necessarythis.booleanInput = new Ext.form.Checkbox({hideParent: true,hidden: true});this.intInput = new Ext.form.NumberField({allowDecimals: false,hideParent: true,hidden: true});this.floatInput = new Ext.form.NumberField({hideParent: true,hidden: true});this.textInput = new Ext.form.TextField({hideParent: true,hidden: true});this.dateInput = new Ext.form.DateField({hideParent: true,hidden: true});this.items = [ this.fieldCombo, this.testCombo, this.booleanInput, this.intInput, this.floatInput, this.textInput, this.dateInput];Ext.ux.FilterCondition.superclass.initComponent.apply(this, arguments);},onFieldSelect: function(combo, rec, index) {this.booleanInput.hide();this.intInput.hide();this.floatInput.hide();this.textInput.hide();this.dateInput.hide();var t = rec.get('type');if (t == 'boolean') {this.booleanInput.show();this.valueInput = this.booleanInput;} else if (t == 'int') {this.intInput.show();this.valueInput = this.intInput;} else if (t == 'float') {this.floatInput.show();this.valueInput = this.floatInput;} else if (t == 'date') {this.dateInput.show();this.valueInput = this.dateInput;} else {this.textInput.show();this.valueInput = this.textInput;}},getValue: function() {return {field: this.fieldCombo.getValue(),test: this.testCombo.getValue(),value: this.valueInput.getValue()};} });此類管理了其包含的輸入字段,可以精確的布局-大小調整,外補丁等等——都是通過CSS樣式分配到元素身上這樣來起作用的。
位于該層次的對象還提供了特別的模板方法:
- onBeforeAdd
Panel
如果所需的UI控件要求頭部、底部、或工具條之類的元素,那么Ext.Panel就是一個很不錯的類給予繼承了。
注意: Panel是容器的一種,不要忘記哪種布局類是被用于渲染和調控子組件的。
通常Ext.Panel所實現的類會有很高的程序結合性,一般用于與其他UI控件協調使用(通常Containers,或表單字段),并對其有特定配置的布局風格。另外,要對在其內的組件提供操作的命令,可以從tbar(頂部工具欄),bbar(底部工具欄)的兩方面設置加以控制。
Field
如果所需的UI控件要求為用戶交互,可以把程序的數據顯示給用戶,或修改進而發生給服務器的功能,那么要被擴展的類應該是Ext.form.TextField,或Ext.Form.NumberField。另外,如果要求輪換按鈕(Trigger button),以備鍵盤按鍵的輪換,那就是Ext.form.TriggerField。
位于該層次的對象還提供了特別的模板方法:
- onFocus:input輸入框得到焦點后即會觸發該方法的執行。
- onBlur:input輸入框失去焦點后即會觸發該方法的執行。
什么時候不需要子類
有些時候,濫用子類無異于“殺雞用牛刀”。在一些特定應用場合,某個現有的類它的方法被添加、被重寫,是由這個類的構造器實例化過程中依靠參數傳入的。
轉載于:https://www.cnblogs.com/ibravias/archive/2011/08/11/2134594.html
總結
以上是生活随笔為你收集整理的使用ExtJs创建新的UI控件(转)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 有关 Session 操作的几个误区
- 下一篇: 好代码、坏代码之一