無論你是Ext庫的新手,抑或是想了解Ext的人,本篇文章的內(nèi)容都適合你。本文將簡(jiǎn)單地介紹Ext的幾個(gè)基本概念,和如何快速地做出一個(gè)動(dòng)態(tài)的頁面并運(yùn)行起來,假設(shè)讀者已具備了一些JavaScript經(jīng)驗(yàn)和簡(jiǎn)單了解HTML的文檔對(duì)象模型(document object model ,DOM)。
下載Ext
如果你未曾下載過,那應(yīng)從官方網(wǎng)站那里下載最新版本的Ext http://extjs.com/downloads 。
因應(yīng)各種的下載需求,有幾種不同的可選項(xiàng)。通常地,最穩(wěn)定的版本,是較多人的選擇。下載解包后,那個(gè)example文件夾便是一個(gè)探索Ext的好地方!
開始!
下載示例文件 |
我們將講講怎么使用Ext,來完成一些JavaScript常見的任務(wù)。如果你想自己試試,就應(yīng)該先下IntroToExt2.zip ,用來構(gòu)建已下面的Ext代碼。
Zip包里有三個(gè)文件:ExtStart.html, ExtStart.js和ExtStart.css。解包這三個(gè)文件到Ext的安裝目錄中(例如,Ext是在“C:\code\Ext\v2.0”中,那應(yīng)該在"v2.0"里面新建目錄“tutorial”。雙擊ExtStart.htm,接著你的瀏覽器打開啟動(dòng)頁面,應(yīng)該會(huì)有一條消息告訴你配置已完畢。如果出現(xiàn)了Javascript錯(cuò)誤,請(qǐng)按照頁面上的指引操作。
現(xiàn)在在你常用的IDE中或文本編輯器中,打開ExtStart.js看看。
Ext.onReady(function() { alert("Congratulations! You have Ext configured correctly!");
});
Ext.onReady可能是你接觸的第一個(gè)也可能是在每個(gè)頁面都要使用的方法。這個(gè)方法會(huì)在DOM加載全部完畢后,保證頁面內(nèi)的所有元素能被Script引用(reference)之后調(diào)用。你可刪除alert()那行,加入一些實(shí)際用途的代碼試試。
Element:Ext的核心
大多數(shù)的JavaScript操作都需要先獲取頁面上的某個(gè)元素的引用(reference),好讓你來做些實(shí)質(zhì)性的事情。傳統(tǒng)的JavaScript做法,是通過ID獲取Dom節(jié)點(diǎn)的:
var myDiv = document.getElementById('myDiv');
這毫無問題,不過這樣單單返回一個(gè)對(duì)象(DOM節(jié)點(diǎn)),用起來并不是太實(shí)用和方便。為了要用那節(jié)點(diǎn)干點(diǎn)事情,你還將要手工編寫不少的代碼;另外,對(duì)于不同類型瀏覽器之間的差異,要處理起來可真頭大了。
進(jìn)入Ext.element 對(duì)象。元素(element)的的確確是Ext的心臟地帶,--無論是訪問元素(elements)還是完成一些其他動(dòng)作,都要涉及它。Element的API是整個(gè)Ext庫的基礎(chǔ),如果你時(shí)間不多,只是想了解Ext中的一兩個(gè)類的話,Element一定是首選!
由ID獲取一個(gè)Ext Element如下(首頁ExtStart.htm包含一個(gè)div,ID名字為“myDiv”,然后,在ExtStart.js中加入下列語句): The corresponding code to get an Ext Element by ID looks like this (the starter page ExtStart.html contains a div with the id "myDiv," so go ahead and add this code to ExtStart.js):
Ext.onReady(function() { var myDiv = Ext.get('myDiv');
});
再回頭看看Element對(duì)象,發(fā)現(xiàn)什么有趣的東東呢?
- Element包含了常見的DOM方法和屬性,提供一個(gè)快捷的、統(tǒng)一的、跨瀏覽器的接口(若使用Element.dom的話,就可以直接訪問底層DOM的節(jié)點(diǎn)。);
- Element.get()方法提供內(nèi)置緩存(Cache),多次訪問同一對(duì)象效率上有極大優(yōu)勢(shì);
- 內(nèi)置常用的DOM節(jié)點(diǎn)的動(dòng)作,并且是跨瀏覽器的定位的位置、大小、動(dòng)畫、拖放等等(add/remove CSS classes, add/remove event handlers, positioning, sizing, animation, drag/drop)。
這意味著你可用少量的代碼來做各種各樣的事情,這里僅僅是一個(gè)簡(jiǎn)單的例子(完整的列表在Element API 文檔中)。
繼續(xù)在ExtStart.js中,在剛才我們獲取好myDiv的位置中加入:
myDiv.highlight(); //黃色高亮顯示然后漸退 myDiv.addClass('red'); // 添加自定義CSS類 (在ExtStart.css定義) myDiv.center(); //在視圖中將元素居中 myDiv.setOpacity(.25); // 使元素半透明
獲取多個(gè)DOM的節(jié)點(diǎn)
通常情況下,想獲取多個(gè)DOM的節(jié)點(diǎn),難以依靠ID的方式來獲取。有可能因?yàn)闆]設(shè)置ID,或者你不知道ID,又或者直接用ID方式引用有太多元素了。這種情況下,你就會(huì)不用ID來作為獲取元素的依據(jù),可能會(huì)用屬性(attribute)或CSS Classname代替。基于以上的原因,Ext引入了一個(gè)異常強(qiáng)大的Dom Selector庫,叫做DomQuery。
DomQuery可作為單獨(dú)的庫使用,但常用于Ext,你可以在上下文環(huán)境中(Context)獲取多個(gè)元素,然后通過Element接口調(diào)用。令人欣喜的是,Element對(duì)象本身便有Element.selcect的方法來實(shí)現(xiàn)查詢,即內(nèi)部調(diào)用DomQuery選取元素。這個(gè)簡(jiǎn)單的例子中,ExtStart.htm包含若干段落(
標(biāo)簽),沒有一個(gè)是有ID的,而你想輕松地通過一次操作馬上獲取每一段,全體執(zhí)行它們的動(dòng)作,可以這樣做:
// 每段高亮顯示 Ext.select('p').highlight();
Element.select在這個(gè)例子中的方便性顯露無疑。它返回一個(gè)復(fù)合元素,能通過元素接口(Element interface)訪問每個(gè)元素。這樣做的好處是可不用循環(huán)和不分別訪問每一個(gè)元素。
DomQuery的選取參數(shù)可以是一段較長(zhǎng)的數(shù)組,其中包括W3C CSS3 Dom選取器、基本XPatch、HTML屬性和更多,請(qǐng)參閱DomQuery API文檔以了解這功能強(qiáng)大的庫個(gè)中細(xì)節(jié)。
響應(yīng)事件
到這范例為止,我們所寫的代碼都是放在onReady中,即當(dāng)頁面加載后總會(huì)立即執(zhí)行,功能較單一——這樣的話,你便知道,如何響應(yīng)某個(gè)動(dòng)作或事件來執(zhí)行你希望做的事情,做法是,先分配一個(gè)function,再定義一個(gè)event handler事件處理器來響應(yīng)。我們由這個(gè)簡(jiǎn)單的范例開始,打開ExtStart.js,編輯下列的代碼:
Ext.onReady(function() { Ext.get('myButton').on('click', function(){ alert("你單擊了按鈕");
});
});
代碼依然會(huì)加載好頁面后執(zhí)行,不過重要的區(qū)別是,包含alert()的function是已定義好的,但它不會(huì)立即地被執(zhí)行,是分配到按鈕的單擊事件中。用淺顯的文字解釋,就是:獲取ID為'myDottom'元素的引用,監(jiān)聽任何發(fā)生這個(gè)元素上被單擊的情況,并分配一個(gè)function,以準(zhǔn)備任何單擊元素的情況。
一般來說,Element.select也能做同樣的事情,即作用在獲取一組元素上。下一例中,演示了頁面中的某一段落被單擊后,便有彈出窗口:
Ext.onReady(function() { Ext.select('p').on('click', function() { alert("你單擊一段落;");
});
});
這兩個(gè)例子中,事件處理的function均是簡(jiǎn)單幾句,沒有函數(shù)的名稱,這種類型函數(shù)稱為“匿名函數(shù)(anonymous function)”,即是沒有名的的函數(shù)。你也可以分配一個(gè)有名字的event handler,這對(duì)于代碼的重用或多個(gè)事件很有用。下一例等效于上一例:
Ext.onReady(function() { var paragraphClicked = function() { alert("You clicked a paragraph");
} Ext.select('p').on('click', paragraphClicked);
});
到目前為止,我們已經(jīng)知道如何執(zhí)行某個(gè)動(dòng)作。但當(dāng)事件觸發(fā)時(shí),我們?nèi)绾蔚弥@個(gè)event handler執(zhí)行時(shí)是作用在哪一個(gè)特定的元素上呢?要明確這一點(diǎn)非常簡(jiǎn)單,Element.on方法傳入到even handler的function中(我們這里先討論第一個(gè)參數(shù),不過你應(yīng)該瀏覽API文檔以了解even handler更多的細(xì)節(jié))。在我們之前的例子中,function是忽略這些參數(shù)的,到這里可有少許的改變,——我們?cè)诠δ苌咸峁┝烁顚哟蔚目刂啤1仨毾日f明的是,這實(shí)際上是Ext的事件對(duì)象(event object),一個(gè)跨瀏覽器和擁有更多控制的事件的對(duì)象。例如,可以用下列的語句,得到這個(gè)事件響應(yīng)所在的DOM節(jié)點(diǎn):
Ext.onReady(function() { var paragraphClicked = function(e) { Ext.get(e.target).highlight();
} Ext.select('p').on('click', paragraphClicked);
});
注意得到的e.target是DOM節(jié)點(diǎn),所以我們首先將其轉(zhuǎn)換成為EXT的Elemnet元素,然后執(zhí)行欲完成的事件,這個(gè)例子中,我們看見段落是高亮顯示的。
使用Widgets
(Widget原意為“小器件”,現(xiàn)指頁面中UI控件)
除了我們已經(jīng)討論過的核心JavaScript庫,當(dāng)前的Ext亦包括了一系列的最前端的JavaScirptUI組件庫。文本以一些常用的widget為例子,作簡(jiǎn)單的介紹。
MessageBox
比起略為沉悶的“HelloWolrd”消息窗口,我們做少許變化,前面我們寫的代碼是,單擊某個(gè)段落便會(huì)高亮顯示,現(xiàn)在是單擊段落,在消息窗口中顯示段落內(nèi)容出來。
在上面的paragraphClicked的function中,將這行代碼:
Ext.get(e.target).highlight();
替換為:
var paragraph = Ext.get(e.target);
paragraph.highlight();
Ext.MessageBox.show({ title: 'Paragraph Clicked',
msg: paragraph.dom.innerHTML,
width:400,
buttons: Ext.MessageBox.OK,
animEl: paragraph
});
這里有些新的概念需要討論一下。在第一行中我們創(chuàng)建了一個(gè)局部變量(Local Variable)來保存某個(gè)元素的引用,即被單擊的那個(gè)DOM節(jié)點(diǎn)(本例中,DOM節(jié)點(diǎn)指的是段落paragrah,事因我們已經(jīng)定義該事件與
標(biāo)簽發(fā)生關(guān)聯(lián)的了)。為什么要這樣做呢?嗯...觀察上面的代碼,我們需要引用同一元素來高亮顯示,在MessageBox中也是引用同一元素作為參數(shù)使用。
一般來說,多次重復(fù)使用同一值(Value)或?qū)ο螅且粋€(gè)不好的方式,所以,作為一個(gè)具備良好OO思維的開發(fā)者,應(yīng)該是將其分配到一個(gè)局部變量中,反復(fù)使用這變量!
現(xiàn)在,為了我們接下來闡述新概念的演示,請(qǐng)觀察MessageBox的調(diào)用。乍一看,這像一連串的參數(shù)傳入到方法中,但仔細(xì)看,這是一個(gè)非常特別的語法。實(shí)際上,傳入到MessageBox.show的只有一個(gè)參數(shù):一個(gè)Object literal,包含一組屬性和屬性值。在Javascript中,Object Literal是動(dòng)態(tài)的,你可在任何時(shí)候用{和}創(chuàng)建一個(gè)典型的對(duì)象(object)。其中的字符由一系列的name/value組成的屬性,屬性的格式是[property name]:[property value]。在整個(gè)Ext中,你將會(huì)經(jīng)常遇到這種語法,因此你應(yīng)該馬上消化并吸收這個(gè)知識(shí)點(diǎn)!
使用Object Literal的原因是什么呢?主要的原因是“可伸縮性(flexibility)”的考慮",隨時(shí)可新增、刪除屬性,亦可不管順序地插入。而方法不需要改變。這也是多個(gè)參數(shù)的情況下,為最終開發(fā)者帶來不少的方便(本例中的MessageBox.show())。例如,我們說這兒的foo.action方法,有四個(gè)參數(shù),而只有一個(gè)是你必須傳入的。本例中,你想像中的代碼可能會(huì)是這樣的foo.action(null, null, null, 'hello').,若果那方法用Object Literal來寫,卻是這樣, foo.action({ param4: 'hello' }),這更易用和易讀。
Grid
Grid是Ext中人們最想先睹為快的和最為流行Widgets之一。好,讓我們看看怎么輕松地創(chuàng)建一個(gè)Grid并運(yùn)行。用下列代碼替換ExtStart.js中全部語句:
Ext.onReady(function() { var myData = [ ['Apple',29.89,0.24,0.81,'9/1 12:00am'],
['Ext',83.81,0.28,0.34,'9/12 12:00am'],
['Google',71.72,0.02,0.03,'10/1 12:00am'],
['Microsoft',52.55,0.01,0.02,'7/4 12:00am'],
['Yahoo!',29.01,0.42,1.47,'5/22 12:00am'] ];
var myReader = new Ext.data.ArrayReader({}, [ {name: 'company'},
{name: 'price', type: 'float'},
{name: 'change', type: 'float'},
{name: 'pctChange', type: 'float'},
{name: 'lastChange', type: 'date', dateFormat: 'n/j h:ia'} ]);
var grid = new Ext.grid.GridPanel({ store: new Ext.data.Store({ data: myData, reader: myReader }),
columns: [ {header: "Company", width: 120, sortable: true, dataIndex: 'company'},
{header: "Price", width: 90, sortable: true, dataIndex: 'price'},
{header: "Change", width: 90, sortable: true, dataIndex: 'change'},
{header: "% Change", width: 90, sortable: true, dataIndex: 'pctChange'},
{header: "Last Updated", width: 120, sortable: true,
renderer: Ext.util.Format.dateRenderer('m/d/Y'),
dataIndex: 'lastChange'} ],
viewConfig: { forceFit: true },
renderTo: 'content',
title: 'My First Grid',
width: 500,
frame: true });
grid.getSelectionModel().selectFirstRow();
});
這看上去很復(fù)雜,但實(shí)際上加起來,只有四行代碼(不包含測(cè)試數(shù)據(jù)的代碼)。
- 第一行創(chuàng)建數(shù)組并作為數(shù)據(jù)源。實(shí)際案例中,你很可能從數(shù)據(jù)庫、或者WebService那里得到動(dòng)態(tài)的數(shù)據(jù)。
- 接著,我們創(chuàng)建并加載data store, data store將會(huì)告訴Ext的底層庫接手處理和格式化這些數(shù)據(jù)。不同的數(shù)據(jù)類型須在類Reader中指明。
- 接著,我們創(chuàng)建一個(gè)Grid的組件,傳入各種的配置值,有:
- 新的data store, 配置好測(cè)試數(shù)據(jù)和reader
- 列模型column model定義了 列columns的配置
- 其他的選擇指定了Grid所需的功能
- 最后,通過SelectionModel告訴Grid高亮顯示第一行。
不是太困難吧?如果一切順利,完成之后你會(huì)看到像這樣的:
當(dāng)然,你現(xiàn)在還未掌握這段代碼的某些細(xì)節(jié),但先不要緊,這個(gè)例子的目的是告訴你,只要學(xué)習(xí)了少量的幾行代碼,創(chuàng)建一個(gè)富界面的多功能的UI組件是可能的。更多的grid細(xì)節(jié)讀者可作為一種練習(xí)去學(xué)習(xí)。這兒有許多學(xué)習(xí)Grid的資源,Ext Grid教程、Grid交互演示交和Grid API文檔。
還有更多的..
這只是冰山一角。還有一打的UI Widgets可以供調(diào)用,如 layouts, tabs, menus, toolbars, dialogs, tree view等等。請(qǐng)?zhí)剿?范例演示。
編輯 使用Ajax
在弄好一些頁面后,你已經(jīng)懂得在頁面和腳本之間的交互(interact)原理。接下來,你應(yīng)該掌握的是,怎樣與遠(yuǎn)程服務(wù)器(remote server)交換數(shù)據(jù),常見的是從數(shù)據(jù)庫加載數(shù)據(jù)(load)或是保存數(shù)據(jù)(save)到數(shù)據(jù)庫中。通過JavaScript異步無刷新交換數(shù)據(jù)的這種方式,就是所謂的Ajax。Ext內(nèi)建卓越的Ajax支持,例如,一個(gè)普遍的用戶操作就是,異步發(fā)送一些東西到服務(wù)器,然后,UI元素根據(jù)回應(yīng)(Response)作出更新。這是一個(gè)包含text input的表單,一個(gè)div用于顯示消息(注意,你可以在ExtStart.html中加入下列代碼,但這必須要訪問服務(wù)器): 接著,我們加入這些處理交換數(shù)據(jù)的JavaScript代碼到文件ExtStart.js中(用下面的代碼覆蓋): 注意: 這個(gè)例子需要web server才可運(yùn)行。 瀏覽器的URL地址不應(yīng)是以file://開頭,而是http://開頭,否則的話Ajax的數(shù)據(jù)交互將不會(huì)工作。Localhost就可以工作得很好,但必須是通過http的。 這種模式看起來已經(jīng)比較熟悉了吧!先獲取按鈕元素,加入一個(gè)匿名函數(shù)監(jiān)聽單擊。在事件處理器中(event handler),我們使用一個(gè)負(fù)責(zé)處理Ajax請(qǐng)求、接受響應(yīng)(Response)和更新另一個(gè)元素的Ext內(nèi)建類,稱作UpdateManagerUpdater。UpdateManager可以直接使用,或者和我們現(xiàn)在的做法一樣,通過Element的load方法來引用(本例中該元素是id為“msg“的div)。當(dāng)使用Element.load方法,請(qǐng)求(request)會(huì)在加工處理后發(fā)送,等待服務(wù)器的響應(yīng)(Response),來自動(dòng)替換元素的innerHTML。簡(jiǎn)單傳入服務(wù)器url地址,加上字符串參數(shù),便可以處理這個(gè)請(qǐng)求(本例中,參數(shù)值來自“name”元素的value),而text值是請(qǐng)求發(fā)送時(shí)提示的文本,完畢后顯示那個(gè)msg的div(因?yàn)殚_始時(shí)默認(rèn)隱藏)。當(dāng)然,和大多數(shù)Ext組件一樣,Ext.Ajax 有許多的參數(shù)可選,不同的Ajax請(qǐng)求有不同的方案。而這里僅演示最簡(jiǎn)單的那種。 最后一個(gè)關(guān)于Ajax隱晦的地方就是,服務(wù)器實(shí)際處理請(qǐng)求和返回(Resposne)是具體過程。這個(gè)過程會(huì)是一個(gè)服務(wù)端頁面,一個(gè)Servlet,一個(gè)Http調(diào)度過程,一個(gè)WebService,甚至是Perl或CGI腳本,即不指定一個(gè)服務(wù)器都可以處理的http請(qǐng)求。讓人無法預(yù)料的是,服務(wù)器返回什么是服務(wù)器的事情,無法給一個(gè)標(biāo)準(zhǔn)的例子來覆蓋闡述所有的可能性。。 下面的例子是一些常見的語言以方便開始測(cè)試(這段代碼輸出剛才我們傳入'name'的那個(gè)值到客戶端,即發(fā)送什么,返回什么,然后在我們剛才寫的'msg' div中加入該文本)。PHP的已經(jīng)包含在下載文件中,文件名為'ajax-example.php',可換成你自己服務(wù)端的代碼: 使用Ajax的真正挑戰(zhàn),是需要進(jìn)行適當(dāng)?shù)氖止ぞ幋a,并相應(yīng)格式化為服務(wù)端可用接受的數(shù)據(jù)結(jié)構(gòu)。有幾種格式供人們選擇(最常用為JSON/XML)。 Ext沒有跟任何服務(wù)器語言有獨(dú)家聯(lián)系,因此其它特定語言的庫亦可用于Ext處理Ajax服務(wù)。只要頁面接受到結(jié)果是EXT能處理的數(shù)據(jù)格式,Ext絕不會(huì)干涉服務(wù)器其他的事情!推薦參閱我們提供的各個(gè)平臺(tái)資源以了解服務(wù)端框架的更多資訊和輔助。 現(xiàn)在你已經(jīng)一睹Ext其芳容,知道她大概能做些什么了。下面的資源有助您進(jìn)一步深入了解:
<div id="msg"></div>
<div>
Name: <input type="text" id="name" />
<input type="button" id="okButton" value="OK" />
</div>
<div id="msg"></div>
Ext.onReady(function(){
Ext.get('okButton').on('click', function(){
var msg = Ext.get('msg');
msg.load({
url: 'ajax-example.php', // <-- 按實(shí)際改動(dòng)
params: 'name=' + Ext.get('name').dom.value,
text: 'Updating...'
});
msg.show();
});
});
Plain PHP
<?php if(isset($_POST['name'])) {
echo 'From Server: '.$_POST['name'];
}
?>
CakePHP
<?php if(isset($this->data['name'])) {
$this->flash('From Server: '.$this->data['name']);
}
?>
Django
from django.http import HttpResponse
def ajax_request(request):
return HttpResponse('From Server: %s' % request.POST.get('name', 'nada'))
Perl
#!/usr/bin/perl
use strict;
use warnings;
use CGI;
my $Query = new CGI;
print $Query->header();
print "Hello from : ".$Query->param('name');
exit;
ASP.Net
protected void Page_Load(object sender, EventArgs e)
{
if (Request.Form["name"] != null)
{
Response.Write("From Server: " + Request.Form["name"]);
Response.End();
}
}
ColdFusion
<cfif StructKeyExists(form, "name")>
<cfoutput>From Server: #form.name#</cfoutput>
</cfif>
JSP
From Server: ${param.name}
下一步是?