2009年8月31日 星期一

ext-doc


解開後在sample目錄下有ext-doc.bat執行檔,以及sample code(sample.js)和configure檔(ext.xml)。可以將js檔加入ext.xml的source裡面,至於document的描述可以參考sample code或者extjs的source file。設定好ext.xml後,直接執行ext-doc.bat就可以產生結果啦,預設的輸出目錄是在output的目錄下。接著把output複製到web server就可以看結果啦。

下載 ext-doc-1.0.131

相關網址:
http://code.google.com/p/ext-doc/downloads/list
http://extjs.com/forum/showthread.php?t=55214
http://groups.google.com/group/ext-doc?pli=1
http://ext-doc.org/docs/



2009年8月24日 星期一

新竹 - 日式料理 - 田秋屋 ☆☆


這家店位於新竹市新莊街上,這邊的感覺讓人覺得高貴不貴,因為東西真的不貴,不過我看了一下Menu,感覺好像有調漲過,不知道是不是這次88水災的關係,不過整體用餐感覺還不錯啦。
我點了味噌鯖魚定食,魚肉夠大片,而且口感超好,還附了味噌湯和沙拉以及茶碗蒸。
另外還點了一份鮭魚飯定食,配菜都一樣,連茶碗蒸上面的擺飾都長的一樣,哈哈哈哈。
三十元的炸豆腐是大家都推薦的,油而不膩,口感外酥內嫩,一定要趁熱吃。
附上菜單,給有興趣的老饕看一下,下迴也來點點他的壽司好了,看起來都還不錯。
類別:日式料理。 店名:田秋屋。 地點:新竹市新莊街六號。 評論:☆☆ 在新竹要找到便宜又好吃的店家真的不多,所以這家店還蠻值得推薦的,下回再點點沒吃過炭烤類吧。

2009年8月18日 星期二

lex & yacc - lex introduction


lex被稱為Lexical Analyzer(中文要叫做語彙分析器?有點怪),用來產生辨識字詞的工具,透過regular expression定義pattern,當字詞符合某個pattern,就做特定的action。簡單的說就是切token。 lex檔分成三個部分: 1. definition section(declarations):用於初始化C和lex的,比如變數的宣告。 2. rule section(rules):定義pattern與相對應的action。 3. user subroutine section(programs):就是C code。 %{ /* comment: this is demo code * file name: 01.l */ %} %% [\t ]+ /* ignore space */ ; hello | world { printf("I can recognize the word \"%s\"\n", yytext); } %% int main() { yylex(); return 0; }
brook@debian:~/src/lex$ flex 01.l -o 01.yy.c
brook@debian:~/src/lex$ flex 01.l
brook@debian:~/src/lex$ gcc lex.yy.c -ll -Wall
lex.yy.c:1085: warning: 'yyunput' defined but not used
lex.yy.c:1128: warning: 'input' defined but not used
brook@debian:~/src/lex$ ./a.out
hello world
hello world! brook
I can recognize the word "hello"
I can recognize the word "world"
!brook
regular expression
.代表任何一個字元,但不含換行(\n)。
*重覆前一個比對零次以上。
[]比對[]中任一個字元。
[^][]的反向。
$每行的結尾。
{n,m}前一個比對至少重複n次,最多m次。
+重覆前一個比對一次以上。
?前一個比對可出現一次或零次。
|or
( )定義subexpression。
比如:  ".*":表示比對任何一個字元零次以上。 [0-9]+:表示比對數字一次以上,如0921等等。 -?[0-9]+:表示負號可出現可不出現,即表示正負數。 ([0-9]+) | ([0-9]+\.[0-9]+):( )分成兩個subexpression,|表示其中一個比對成功即可,也就是整數或小數。 [0-9]{1,3}:表示有1~3個數字。

2009年8月15日 星期六

為台灣致哀


看著電視媒體傳送著台灣50年來最大的災難,心中的難過總是一湧而上,淚水總是在自己的強忍下,停留在眼眶中打轉,望著那些長官們說的鬼話,心中的氣憤更是難平,平常這些辛勞的百姓為生活在大太陽底下工作,而這些高官卻只是在冷氣房裡決定政策,即便是錯誤的政策,而又或者是為了貪汙而制定了哪些莫名其妙的政策,百姓們也都只能苦笑買單。 但是今天,面對多少破碎的家庭,多少妻離子散的畫面,那怕是官員們演個戲也好,為這些苦難的百姓哀悼一下吧,不是他們不願撤離家園,而是誰都不知道有這麼大的災難。再者,如果今天人人都能在冷氣房裡辦公,誰還願意在大太陽底下,踩著滾燙的柏油路,辛勤的工作,再辛苦也是為了那口飯,還有那年幼的小孩,年邁的父母親。我也是一位農家子弟出身,小時候不管颳風下雨或者再大的太陽,總是要把田裡的工作完成,無奈靠天吃飯的農民,可能一個颱風就毀了這段時間的汗水,如果幸運逃過老天的考驗,到了收成的那刻,在田裡所受的大小傷都值得了,但是,這一切也都只能換取全家的溫飽,要多的是不可能的了。即便蔬菜水果再貴,永遠都是批發商在賺大錢,而農夫能感受到的只有曬在那刺痛的身體上的大太陽,還有寒風刺骨的寒流,這一切都夠可憐了,遠在市中心的長官們能感受到嗎?苦民所苦是騙人的。 看著電視,望著一幕一幕的救災畫面,面目可憎的名嘴們、政客們哪一個在第一時間到達災區?哪一個敢深入災區?騙人民的感情與信任,說再多都沒有用,因為災民們需要的是具體的行動,他們在挨餓的時候不會因為你們遠在電視機裡的一句話而飽了,但是他們的親人卻可能因為你晚來了一步而死了,每當時間經過一秒,他們內心的惶恐就多了一分,因為他們想像著親人被壓在土石堆裡,還有那一口氣等待救援,但是卻手無寸鐵,無法將親人在最後一刻救出。如果是你,你能承受嗎? 七天過了,即便人沒有被壓死,也該被活活的餓死渴死了,是政策殺人,卻沒有一個人為此負責,名嘴與政客也沒有在第一時間發揮他們的影響力前去救災,只有小兵們、小老百姓們還在為災民冒著生命危險持續搶救,這才是真正愛這片土地,這塊土地上的偉大英雄,每當畫面看到名嘴與政客的相互批判,讓我著實感到悲哀,為什麼還有人願意相信這群人,他們分不清誰才是真正愛他們的人嗎? 雨過就會天晴,但是人死不能復生,未來的路還相當的漫長,面對可能產生的孤兒,未來的路更顯的崎嶇,想到這心中又不免難過了起來。一夕之間,山河變色,希望的是,未來政府能多照顧這群災民,能讓她們面對自己未來的人生,更要想辦法照顧這些孤兒,因為他們真的是無辜的,他們的生命現在還很脆弱,很可能因為沒了父母,失了照顧,成了社會的邊緣人。

GCC (4.4.1)之C Preprocessor part I


C Preprocessor系列,其實就是CPP manual的讀書心得。

initial processing 在初始化的處理上,CPP主要有四個任務:
  1. 讀取檔案到記憶體中。
  2. 如果有啟動trigraphs,那麼會做trigraph轉換,如??/轉換成\,??-轉成~等等。
  3. 合併連續行成一行,就是每行後面有\當結尾的,會將該行和下一行合併成一行。
  4. 將註解的部分用一個空白取代。
Tokenization CPP使用空白區分token,如果沒有token之間沒有空白,那麼CPP會使用greedy法則,也就是盡可能的找最長字串的token,比如a++++b會被解釋成a++ ++ +b,而不是a++ + ++b,而token之間預設也是一個空白,比如: #define foo() bar foo()baz 會被解釋成bar baz,不管#define中的foo()和bar有幾個空白。
brook@debian:~$ echo -e "#define foo() bar\nfoo()baz"|gcc -E -
# 1 "<stdin>"
# 1 "<built-in>"
# 1 "<command-line>"
# 1 "<stdin>"

bar baz

The preprocessing language 所謂的preprocessing language,就是以#開頭的那些preprocessing directive,這些指令是固定的,無法讓使用者自訂新的。這些指令主要有四種用途:
  1. include of header file(比如#include)。
  2. Macro expansion(比如將#define展開)。
  3. Conditional Compilation(比如#if,決定哪些要被編譯,哪些不被編譯)。
  4. Line control(我不是很清楚,只知道#line num下一行就會由num當行數重新計算,也可以簡寫成# num)。
  5. Diagnostic(比如#error)。


Header files Header files主要分成兩類:
  1. System header files:提供OS部分的interface,<xx.h>。
  2. Your own header file:提供source files的interface,"xx.h"。
傳統上,C的header files都使用.h當結尾,當您在#include 時,其實就是將xx.h複製到該行上,並利用Line control,重新計算後面的行數,比如:
brook@desktop:~$ cat a.h
#ifndef A_H
#define A_H
int hello(char *str);
#endif
brook@desktop:~$ cat a.c
#include 
#include "a.h"

int main(int argc, char * argv[])
{
    printf("hello world\n", __LINE__);

    return 0;
}
brook@desktop:~$ cpp -I./ a.c
... 略 ...
# 2 "a.c" 2
# 1 "a.h" 1


int hello(char *str);
# 3 "a.c" 2

int main(int argc, char * argv[])
{
    printf("hello world\n", 6);

    return 0;
}
看到把a.h中的int hello(char *str)複製到source file中了嗎?隨後又重新計算行數# 3,而header files並沒有規定只能放啥東西,不過通常就是放置一些宣告。 include syntax #include有兩種變形:
  1. #include <file>:for system header files,就是會尋找系統路徑。
  2. #include "file":for your own header files,會先尋找目前目錄,找不到就依循的search path尋找。
注意:有些OS會使用'\'當pathname separator,比如M$,但是還是請使用'/',因為GCC一律都使用'/'當pathname separator。

search path 一般在UNIX底下,system header files的search path為 /usr/local/include libdir/gcc/target/version/include /usr/target/include /usr/include 透過GCC實際觀察一下吧
brook@desktop:~$ gcc -I./ -v a.c
... 略  ...
ignoring nonexistent directory "/usr/local/include/i486-linux-gnu"
ignoring nonexistent directory "/usr/lib/gcc/i486-linux-gnu/4.3.2/../../../../i486-linux-gnu/include"
ignoring nonexistent directory "/usr/include/i486-linux-gnu"
#include "..." search starts here:
#include <...> search starts here:
 ./
 /usr/local/include
 /usr/lib/gcc/i486-linux-gnu/4.3.2/include
 /usr/lib/gcc/i486-linux-gnu/4.3.2/include-fixed
 /usr/include
End of search list.

... 略  ...
您可以透過-I新增search path,新增的search path會優先被尋找,但是如果新增的search path已經在預設的(系統的)search path中,該search path會被忽略,避免影響預設的search path順序。有興趣可以再研究一下-I-和-iquote。

Once-Only headers 一個header files很可能被include兩次,進而造成重複define,引起compile error,標準做法應該是在header file中,使用conditional compilation防止被引入兩次,如:
#ifndef XX_H
#define XX_H
 the entire file
#endif /* !XX_H */
XX_H我們稱為controlling macro或guard macro。macro name不應該使用'_'開頭的命名方式,而在system header files中應該使用'__'開頭,避免與user program造成衝突。 computed include 有時候我們會需要根據不同的configure引入不同的header files,比如:
#if SYSTEM_1
#include "system1.h"
#elif SYSTEM_2
#include "system2.h"
#elif
... 略 ...
#endif
像這樣需要經過計算然後才引入的行為就稱為computed include,你可以發現很就會被許多的#elif淹沒,你可以使用簡單的方式替代,比如:
#define SYSTEM1_H <stdio.h>
#define SYSTEM2_H "system3.h"
... 略 ...
#include SYSTEM1_H
#include SYSTEM2_H
甚至SYSTEM_H可以由Makefile傳遞進來。

System Headers是用來宣告OS和runtime libraries的interface,system header所產生的warning除了#warning會顯示出來,其他的都會被抑制住。一般而言,當GCC在編譯時就已經設定哪些目錄會被當成system header存放的目錄了,不過我們還是可以透過-isystem#pragma GCC system_header兩種方式,將一般的header file當成system header file。
  • -isystem後面接的目錄會被當成system header,用法上和-I一樣。
  • #pragma GCC system_header這個指令告訴GCC把這個header file當成system header。

brook@ubuntu:~$ cat syshdr.c 
#include <stdio.h>
#include "syshdr1.h"
#include "syshdr2.h"
#include "syshdr3.h"

int main(int argc, char *argv[])
{
    syshdr1();
    syshdr2();
    syshdr3();
    return 0;
}
brook@ubuntu:~$ cat syshdr/syshdr1.h 
#ifndef SYSHDR1_H
#define SYSHDR1_H
int syshdr1(void) { printf("%s\n", __FUNCTION__); }
#endif
brook@ubuntu:~$ cat syshdr2.h 
#ifndef SYSHDR2_H
#define SYSHDR2_H
#pragma GCC system_header
int syshdr2(void) { printf("%s\n", __FUNCTION__); }
#endif
brook@ubuntu:~$ cat syshdr3.h 
#ifndef SYSHDR3_H
#define SYSHDR3_H
int syshdr3(void) { printf("%s\n", __FUNCTION__); }
#endif
brook@ubuntu:~$ gcc -Wall -isystem syshdr syshdr.c -o my_syshdr
syshdr.c: In function ‘syshdr3’:
syshdr3.h:3: warning: control reaches end of non-void function



2009年8月12日 星期三

extjs - Ext.PagingToolbar簡介


隨著records的增加,Browser會花更多的時間在畫grid,Ext.PagingToolbar主要用來處理這樣的問題,透過傳遞參數的方式(start:第幾筆開始,limit:共要幾筆),告知Server要傳送哪些資料。
注意,Ext.PagingToolbar只是告知Server要顯示的資料為何,而Ext.PagingToolbar預設會顯示所有的資料。

因為要讀取Server的資料,所以Store.proxy改用Ext.data.HttpProxy。而讀取的格式我們打算用json,所以,store.reader要用Ext.data.JsonReader。
我們的json file(phone_num.json)如下:
{
    "success": true,
    "results": 12, // how many entry will be sent.
    "rows": [ // *Note: this must be an Array
        { "city": "台北市", "num": "02"},
        { "city": "新竹市", "num": "03"},
        { "city": "苗栗縣", "num": "037"},
        { "city": "台中市", "num": "04"},
        { "city": "南投縣", "num": "049"},
        { "city": "嘉義市", "num": "05"},
        { "city": "台南市", "num": "06"},
        { "city": "高雄市", "num": "07"},
        { "city": "屏東縣", "num": "08"},
        { "city": "台東縣", "num": "089"},
        { "city": "馬祖", "num": "0836"},
        { "city": "烏坵", "num": "082"}
    ]
}

Ext.onReady(function() {
    var cm = new Ext.grid.ColumnModel([
        new Ext.grid.RowNumberer(),
        {
            header: 'City',
            dataIndex: 'city'
        }, {
            header: 'Num',
            dataIndex: 'num'
        }
    ]);

    var store = new Ext.data.Store({
        proxy: new Ext.data.HttpProxy({url: 'phone_num.json'}),
        reader: new Ext.data.JsonReader({
            totalProperty: 'results',
            root: 'rows',
            fields: [{name: 'city'}, {name: 'num'}]
        })
    });
    store.load();

    var grid = new Ext.grid.GridPanel({
        renderTo: Ext.getBody(),
        store: store,
        cm: cm,
        sm: new Ext.grid.RowSelectionModel(),
        viewConfig: {
            forceFit: true
        },
        title: "區碼",
        loadMask: true,
        height: 200,
        width: 330,
        bbar: new Ext.PagingToolbar({
            pageSize: 5,
            store: store,
            displayInfo: true
        })
    });
});


由於,我們在Server-Side都只是把整個資料丟給client,所以,grid即便有Ext.PagingToolbar,但是還是會顯示所有由Server丟過來的資料。

Paging with Local Data
在local paging官方網站上有提到兩種方式:
  1. Ext.ux.data.PagingStore
  2. Paging Memory Proxy(examples/ux/PagingMemoryProxy.js)

根據Ext.ux.data.PagingStore作者的說法,PagingMemoryProxy缺點如下:
  1. You have to write extra code to remote load the data for the proxy.
  2. query, filter and collect only work on the current page. You have to write extra code to use the PagingMemoryProxy filter support.
  3. Local sorting works, but you need to set remoteSort:true. There is no remote sorting support.
  4. Added and removed records are only remembered for the current page.
  5. Changing the page is relatively slow (PagingMemoryProxy reprocesses all data).
檔案下載與詳細討論:
PagingStore.js for ext-js v3
PagingStore.js for ext-js v2

以下是v2的code:
Ext.onReady(function() {
    var cm = new Ext.grid.ColumnModel([
        new Ext.grid.RowNumberer(),
        {header: 'City', dataIndex: 'city'},
        {header: 'Num', dataIndex: 'num'}
    ]);

    var store = new Ext.ux.data.PagingStore({
        proxy: new Ext.data.HttpProxy({url: 'phone_num.json'}),
        reader: new Ext.data.JsonReader({
            root: 'rows',
            totalRecords: 'results',
            fields: [{name: 'city'}, {name: 'num'}]
        })
    });
    store.load({params:{start:0, limit: 4}});

    var grid = new Ext.grid.GridPanel({
        renderTo: Ext.getBody(),
        store: store,
        cm: cm,
        sm: new Ext.grid.RowSelectionModel(),
        viewConfig: {
            forceFit: true
        },
        title: "區碼",
        loadMask: true,
        height: 200,
        width: 330,
        bbar: new Ext.PagingToolbar({
            pageSize: 4,
            store: store,
            displayInfo: true
        })
    });
});


可以看到,Server一樣丟全部的資料,但是Grid已經可以以paging的方式顯示了。


2009年8月11日 星期二

誤用比不會用更可怕


最近因為研究extjs,所以花了些時間去研究一下Java Script, 才發現其實Java Script一直讓人誤解為非常簡單且基本的語言,原來是寫Java Script的大多應用在Web上,而且都只是執行非常簡單的功能,再加上大家都沒有花時間去研究這一語言,造成Java Script成為被誤解最深的語言。後來終於因為Ajax的流行,造成許多的Java Script Library的大量發展(如extjs或jquery),我們才有動力真正的想去了解Java Script。其實速食文化的興起,很多人都想(code)寫得少(function)做得多,很多都是抄來抄去的,根本就沒有去了解整個來龍去脈,進而導致誤用,很多也都沒被發現,甚至還為這個bug做workaround。 不過有時候這問題是沒有找到一本好的書,又是因為那該死的速食文化,造成輕鬆學會xxx或這24hr學會xxx之類的書大賣,而其他的書卻是又貴又不好賣,大師們也就不願意花時間去寫一本好書,更甚者,連課堂上也開始講求短時間內學會哪些東西,上了一個月的C,就可以在園區當RD,拚工時,拚廉價,那麼花大把時間在研究的人怎麼能堪的住哩,又有誰願意花時間去研究,畢竟大家都是夠用就好,而研究是想讓原本的東西更好。 總結上述,當您在使用任何東西時,應該要對其有一定程度的了解,至少先翻翻使用手冊吧,不要一在的抄襲前人的東西,而不知道為何要這樣做。好的書是值得大家支持的,不然好的書會越來越少。

extjs - Ext.grid.EditorGridPanel簡介


Ext.grid.EditorGridPanel繼承自Ext.grid.GridPanel,提供編輯Cell的能力。除了將Ext.grid.GridPanel改變成Ext.grid.EditorGridPanel外,還要設定Ext.grid.ColumnModel的欄位的editor。
Ext.onReady(function() {
    var data = [['Brook', '0921'], ['Rene', '0918']];

    var cm = new Ext.grid.ColumnModel([
        new Ext.grid.RowNumberer(),
        {header: 'Name', dataIndex: 'name',
            editor: new Ext.grid.GridEditor(new Ext.form.TextField())},
        {header: 'Tel', dataIndex: 'tel',
            editor: new Ext.grid.GridEditor(new Ext.form.TextField())}
    ]);

    var store = new Ext.data.Store({
        proxy: new Ext.data.MemoryProxy(data),
        reader: new Ext.data.ArrayReader({},[
            {name: 'name'},
            {name: 'tel'}
        ])
    });
    store.load();

    var grid = new Ext.grid.EditorGridPanel({
        renderTo: Ext.getBody(),
        store: store,
        cm: cm,
        sm: new Ext.grid.RowSelectionModel(),
        viewConfig: {
            forceFit: true
        },
        title: "Brook's address book",
        loadMask: true,
        height: 200,
        width: 330
    });
});



新增/刪除
我們在tbar新增兩個按鈕add/remove,add的流程如下:
  1. 停止grid的編輯。
  2. 接著插入一筆空白的資料到第一列的位置。
  3. 將第一行第一列變成編輯模式。

刪除的流程如下:
  1. 取得所有選取的列。
  2. 反覆的將所有選取列,移到store.removed(暫存起來,不然後面會遺失移除的列)。
Ext.onReady(function() {
    var record = Ext.data.Record.create([
        {name: 'name', type: 'string'},
        {name: 'tel', type: 'string'}
    ]);

    var data = [['Brook', '0921'], ['Rene', '0918']];

    var cm = new Ext.grid.ColumnModel([
        {header: 'Name', dataIndex: 'name', 
             editor: new Ext.grid.GridEditor(new Ext.form.TextField())},
        {header: 'Tel', dataIndex: 'tel', 
             editor: new Ext.grid.GridEditor(new Ext.form.TextField())}
    ]);

    var store = new Ext.data.Store({
        proxy: new Ext.data.MemoryProxy(data),
        reader: new Ext.data.ArrayReader({},[
            {name: 'name'},
            {name: 'tel'}
        ]),
        removed: [] /* 放置移除的列 */
    });
    store.load();

    var grid = new Ext.grid.EditorGridPanel({
        renderTo: Ext.getBody(),
        store: store,
        cm: cm,
        sm: new Ext.grid.RowSelectionModel(),
        viewConfig: {
            forceFit: true
        },
        title: "Brook's address book",
        loadMask: true,
        height: 200,
        width: 330,
        tbar: new Ext.Toolbar(['-', /* '-' 是按鈕分隔符號 */
            {
                text: 'add',
                handler: function() {
                    var r = new record({
                        name: '',
                        tel: ''
                    });
                    grid.stopEditing();
                    store.insert(0, r);
                    grid.startEditing(0,0);
                }
            }, {
                text: 'remove',
                handler: function() {
                    var rows = grid.getSelectionModel().getSelections();
                    for (var i = 0; i < rows.length; i++) {
                        store.removed.push(rows[i]); /* 暫存 */
                        store.remove(rows[i]); /* 移除 */
                    }
                }
            }
        ])
    });
});


修改
Ext.onReady(function() {
    var data = [['Brook', '0921'], ['Rene', '0918']];
    var cm = new Ext.grid.ColumnModel([
        new Ext.grid.RowNumberer(),
        {header: 'Name', dataIndex: 'name',
            editor: new Ext.grid.GridEditor(new Ext.form.TextField())},
        {header: 'Tel', dataIndex: 'tel',
            editor: new Ext.grid.GridEditor(new Ext.form.TextField())}
    ]);
    var store = new Ext.data.Store({
        proxy: new Ext.data.MemoryProxy(data),
        reader: new Ext.data.ArrayReader({},[{name: 'name'}, {name: 'tel'}])
    });
    store.load();
    var grid = new Ext.grid.EditorGridPanel({
        renderTo: Ext.getBody(),
        store: store,
        cm: cm,
        sm: new Ext.grid.RowSelectionModel(),
        viewConfig: {
            forceFit: true
        },
        title: "Brook's address book",
        loadMask: true,
        height: 200,
        width: 330,
        tbar: new Ext.Toolbar([{
            text: 'show modified',
            handler: function () {
                var records = store.getModifiedRecords();
                for (var i = 0; i < records.length; i++) {
                    alert(Ext.encode(records[i].getChanges()));
                }
            }
        }])
    });
});

Ext.grid.EditorGridPanel提供直接編輯Cell的能力,透過store.getModifiedRecords()可以取得修改過的rows,針對修改過的rows可以再透過getChanges()取得修改過的欄位,被修改的cell會出現紅色的標記。


限制輸入
cell通常都是由Ext.form.Field的Subclasses組成,而這些元件都具有regex的property可以限制使用者的輸入,比如regex:/[0-9]/就是限制只能輸入數字,一但輸入格式不對就會出現紅線啦。更多資訊可以參考一下Ext.form.Field。
Ext.onReady(function() {
    var data = [['Brook', '0921'], ['Rene', '0918']];
    var cm = new Ext.grid.ColumnModel([
        new Ext.grid.RowNumberer(),
        {
            header: 'Name', dataIndex: 'name',
            editor: new Ext.grid.GridEditor(
                    new Ext.form.TextField({allowBlank:false}))
        }, {
            header: 'Tel', dataIndex: 'tel',
            editor: new Ext.grid.GridEditor(
                    new Ext.form.TextField({regex:/[0-9]/}))
        }
    ]);
    var store = new Ext.data.Store({
        proxy: new Ext.data.MemoryProxy(data),
        reader: new Ext.data.ArrayReader(
                {},[{name: 'name'}, {name: 'tel'}])
    });
    store.load();
    var grid = new Ext.grid.EditorGridPanel({
        renderTo: Ext.getBody(),
        store: store,
        cm: cm,
        sm: new Ext.grid.RowSelectionModel(),
        viewConfig: {
            forceFit: true
        },
        title: "Brook's address book",
        loadMask: true,
        height: 200,
        width: 330
    });
});




2009年8月10日 星期一

extjs - Ext.grid.GridPanel簡介


組成元件
要能正確顯示資料,必須包含資料倉儲(Store),資料欄位的定義(ColumnModel)。
首先我們先定義資料欄位:
var cm = new Ext.grid.ColumnModel({
  {header: 'Name', dataIndex: 'name'},
  {header: 'Tel', dataIndex: 'tel'}
});

接著設定store,store包含資料取得方式(Proxy),以及分析方式(reader)。
var data = [
  ['Brook', '0921'],
  ['Rene', '0918']
];

var store = new Ext.data.Store({
  proxy: new Ext.data.MemoryProxy(data),
  reader: new Ext.data.ArrayReader({},[
    {name: 'name'},
    {name: 'tel'}
  ]);
});
store.load(); // 載入資料

最後再將他們和grid整合在一起:
var grid = new Ext.grid.GridPanel({
    renderTo: Ext.getBody(),
    store: store,
    cm: cm,
    viewConfig: {
        forceFit: true // 自動調整欄寬[註1],
    },
    title: "brook's guest book",
    width: 200,
    stripeRows: true,
    loadMask: true,
    height: 120
});


註1:forceFit在EditorGridPanel底下會出現錯誤(EditorGridPanel編輯後欄位錯亂的問題).

欄寬設定
調整欄寬除了forceFit以外,還可以設定autoExpandColumn: id,將特定欄寬expand以符合grid的大小,但如果設定了forceFit,autoExpandColumn將會被忽略。
var cm = new Ext.grid.ColumnModel([
    {
        header:'Name',
        dataIndex:'name'
    }, {
        header:'Tel',
        dataIndex:'tel',
        id: 'tel'
    }
]);

var grid = new Ext.grid.GridPanel({
    renderTo: Ext.getBody(),
    store: store,
    cm: cm,
    title: "brook's guest book",
    loadMask: true,
    height: 120,
    autoExpandColumn: 'tel'
});
因為autoExpandColumn必須指定id,所以cm欄位也必須指定id。


排序
在cm中,將sortable設定為true後,該欄位就可以進行排序。
var cm = new Ext.grid.ColumnModel([
    {
        header:'Name',
        dataIndex:'name',
        sortable: true
    }, {
        header:'Tel',
        dataIndex:'tel',
        id: 'tel'
    }
]);



也可以設定cm.defaultSortable = true,讓所有欄位都可以sort。
也可以預設某欄位排序的方向(由大到小,或由小到大),在store.sortInfo: {field: 'name', direction: "ASC"},就會將name以升冪排列,DESC則會降冪排列。
var store = new Ext.data.Store({
    proxy: new Ext.data.MemoryProxy(data),
    reader: new Ext.data.ArrayReader({}, [
        {name: 'name'},
        {name: 'bd', type: 'date', dateFormat: 'Y-m-d'}
    ]),
    sortInfo: {
        field: 'name',
        direction: 'DESC'
    }
});



日期顯示
在reader中將type設定為date,並且指定date的format,該欄位就會以date方式讀取,顯示的方式則要透過cm的renderer。
Ext.onReady(function() {
    var cm = new Ext.grid.ColumnModel([
        {
            header: 'Name',
            dataIndex: 'name'
        }, {
            header: 'Birthday',
            dataIndex: 'bd',
            renderer: Ext.util.Format.dateRenderer('Y年m月d日')
        }
    ]);

    var data = [
        ['Brook', '1999-12-03'],
        ['Rene', '1998-12-04']
    ];

    var store = new Ext.data.Store({
        proxy: new Ext.data.MemoryProxy(data),
        reader: new Ext.data.ArrayReader({}, [
            {name: 'name'},
            {name: 'bd', type: 'date', dateFormat: 'Y-m-d'}
        ])
    });
    store.load();

    var grid = new Ext.grid.GridPanel({
        renderTo: Ext.getBody(),
        store: store,
        cm: cm,
        viewConfig: {
            forceFit: true
        },
        title: "brook's guest book",
        loadMask: true,
        height: 120,
    });
});



由上例可知,只要透過cm.renderer就可以在顯示時加入一些變化。renderer函數包含value, metadata, css, record, rowIndex, colIndex, store等參數。
  1. value : Object 這個cell的資料
  2. metadata : Object 包含要套用在該CELL的CSS和HTML屬性的物件
  3. record : Ext.data.record 該列的資料物件
  4. rowIndex : Number Row index
  5. colIndex : Number Column index
  6. store : Ext.data.Store The Ext.data.Store object from which the Record was extracted. 

Check box
CheckboxSelectionModel會在該欄位上顯示check box,讓使用者勾選。預設是多選,設定singleSelect為true可以改變為單選。
Ext.onReady(function() {
    var sm = new Ext.grid.CheckboxSelectionModel();
    var cm = new Ext.grid.ColumnModel([
        sm,
        {
            header: 'Name',
            dataIndex: 'name'
        }, {
            header: 'Tel',
            dataIndex: 'tel'
        }
    ]);

    var data = [
        ['Brook', '0921'],
        ['Rene', '0918']
    ];

    var store = new Ext.data.Store({
        proxy: new Ext.data.MemoryProxy(data),
        reader: new Ext.data.ArrayReader({}, [
            {name: 'name'},
            {name: 'tel'}
        ])
    });
    store.load();

    var grid = new Ext.grid.GridPanel({
        renderTo: Ext.getBody(),
        store: store,
        cm: cm,
        viewConfig: {
            forceFit: true
        },
        title: "brook's guest book",
        loadMask: true,
        height: 120,
        width: 200
    });
});



自動顯示行號 自動顯示行號是由Ext.grid.RowNumberer()提供的功能,和CheckboxSelectionModel一樣,放入cm就可以達到自動顯示行號之功能。
var cm = new Ext.grid.ColumnModel([
    new Ext.grid.RowNumberer(),
    {
        header: 'Name',
        dataIndex: 'name'
    }, {
        header: 'Tel',
        dataIndex: 'tel'
    }
]);

Selection Model
在Ext.grid.GridPanel中,Selection Model預設是使用Ext.grid.RowSelectionModel,而且是可以多選,如果是單選就必須設定RowSelectionModel.singleSelect=true。
var grid = new Ext.grid.GridPanel({
    renderTo: Ext.getBody(),
    store: store,
    cm: cm,
    sm: new Ext.grid.RowSelectionModel({singleSelect: true}),
    viewConfig: {
        forceFit: true
    },
    title: "brook's guest book",
    loadMask: true,
    height: 120,
    width: 200
});

被選到的Row,可以透過grid.getSelectionModel().getSelections()取得。
var grid = new Ext.grid.GridPanel({
    renderTo: Ext.getBody(),
    store: store,
    cm: cm,
    sm: new Ext.grid.RowSelectionModel(),
    viewConfig: {
        forceFit: true
    },
    title: "brook's guest book",
    loadMask: true,
    height: 120,
    width: 200,
    buttons: [{
        text:'show select',
        handler: function() {
            var selections = grid.getSelectionModel().getSelections();
            for (var i = 0; i < selections.length; i++) {
                alert(selections[i].get('name') + ' is selection');
            }
        }
    }]
});

也可以選擇Cell Model,EditorGrid的預設Selection Model就是CellSelectionModel。
var grid = new Ext.grid.GridPanel({
    renderTo: Ext.getBody(),
    store: store,
    cm: cm,
    sm: new Ext.grid.CellSelectionModel(),
    viewConfig: {
        forceFit: true
    },
    title: "brook's guest book",
    loadMask: true,
    height: 120,
    width: 200
});



改變cell中的顯示(render)
圖文並茂的網頁是現在的趨勢,您可以透過Ext.grid.ColumnModel.renderer設定顯示cell時,要做哪些變化,比如,我想判斷電話號碼,顯示出電話號碼屬於哪家電信業者,程式碼如下:
Ext.onReady(function() {
    var record = Ext.data.Record.create([
        {name: 'name', type: 'string'},
        {name: 'tel', type: 'string'}
    ]);

    var data = [
        ['Brook', '0921'],
        ['Rene', '0918'],
        ['John', '0980']
    ];

    function renderTel(value, metaData, record, rowIndex, colIndex, store) {
        if ('0921' == value) {
            return '<img src="images/cht.gif"/>' + value;
        } else if ('0918' == value) {
            return '<img src="images/twm.gif"/>' + value;
        }
        return value;
    }

    var cm = new Ext.grid.ColumnModel([
        {header: 'Name', dataIndex: 'name'},
        {header: 'Tel', dataIndex: 'tel', renderer: renderTel}
    ]);

    var store = new Ext.data.Store({
        proxy: new Ext.data.MemoryProxy(data),
        reader: new Ext.data.ArrayReader({},[
            {name: 'name'},
            {name: 'tel'}
        ]),
        removed: []
    });
    store.load();

    var grid = new Ext.grid.GridPanel({
        renderTo: Ext.getBody(),
        store: store,
        cm: cm,
        sm: new Ext.grid.RowSelectionModel(),
        viewConfig: {
            forceFit: true
        },
        title: "Brook's address book",
        loadMask: true,
        height: 200,
        width: 330
    });
});

render function的參數說明如下:
  1. value: 將要顯示到cell的值。
  2. metaData:cell的屬性,包含css。
  3. record:該行資料。
  4. rowIndex:row index。
  5. colIndex:column index。
  6. store:使用中的store。




extjs - Ext.TabPanel 簡介


Ext.TabPanel中文名稱我也不知道稱為啥?不過感覺Tab應該是指標籤的意思,用於將panel分成幾個群組。
Ext.TabPanel算是個基本的container,所以可以將您想顯示的東西放入items中,就可以顯示了。
最基本的TabPanel如下:
Ext.onReady(function() {
    var tp = new Ext.TabPanel({
        renderTo: Ext.getBody(),
        // activeTab: 1,
        width:300,
        height: 200,
        items: [
            {title: 'p1', html: 'content 1'},
            {title: 'p2', html: 'content 2'}
        ]
    });
});

activeTab指出在初始化的時候顯示的頁面,預設是不顯示任何頁面的。

剛剛提到, TabPanel算是container,所以也可以放置其他元件。
Ext.onReady(function() {
    var form = new Ext.form.FormPanel({
        title: 'p2',
        defaultType: 'textfield',
        items: [{
            fieldLabel: 'name'
        }],
        buttons: [{
            text: 'send'
        }]
    });
    var tp = new Ext.TabPanel({
        renderTo: Ext.getBody(),
        activeTab: 1,
        width:300,
        height: 200,
        items: [
            {title: 'p1', html: 'content 1'},
            form // 在第二個頁面放置一個表單元件.
        ]
    });
});


也可以使用autoLoad載入URL(使用Ext.Updater.update Object),當成頁面的內容,如果有script要執行的話,必需將scripts設為true。
Ext.onReady(function() {
    var tp = new Ext.TabPanel({
        renderTo: Ext.getBody(),
        activeTab: 1,
        width:300,
        height: 200,
        items: [
            {title: 'p1', html: 'content 1'},
            {title: 'p2', autoLoad: {url: 'tab.html', scripts: true}}
        ]
    });
});


2009年8月5日 星期三

extjs - Ext.tree.TreePanel 簡介


Ext.tree.TreePanel繼承自Ext.Panel,算是個container,必須給予節點(node)才能形成完整的樹。也就是說在render之前必須設定好root這個node。

空的樹(直接用renderTo())
Ext.onReady(function() {
    var root = new Ext.tree.TreeNode({
        id: "root",
        text:'B_root'
    });
    root.appendChild(new Ext.tree.TreeNode({
        id: 'c1',
        text: 'B_C1'
    }));

    var tree = new Ext.tree.TreePanel({
        renderTo: 't', //要有個div的id為't'
        width: 150,
        height: 50,
        root: root
    });
});

或者(用el+render())
Ext.onReady(function() {
    var root = new Ext.tree.TreeNode({
        id: "root",
        text:'B_root'
    });
    root.appendChild(new Ext.tree.TreeNode({
        id: 'c1',
        text: 'B_C1'
    }));

    var tree = new Ext.tree.TreePanel({
        el: 't', //要有個div的id為't'
        width: 150,
        height: 50,
        root: root
    });
    tree.render();
});

利用setRootNode設定Root
Ext.onReady(function() {
    var root = new Ext.tree.TreeNode({
        id: "root",
        text:'B_root'
    });

    root.appendChild(new Ext.tree.TreeNode({
        id: 'c1',
        text: 'B_C1'
    }));

    var tree = new Ext.tree.TreePanel({
        el: 't', //要有個div的id為't'
        width: 150,
        autoHeight: true // 自動調整高度
    });
    tree.setRootNode(root);
    tree.render();
});

如果想動態變動root請參考https://extjs.net/forum/showthread.php?p=217227#post217227

可以利用Ext.tree.AsyncTreeNod定義樹狀結構,程式碼如下:
Ext.onReady(function() {
    var root = new Ext.tree.AsyncTreeNode({
        id: 'aRoot',
        text: 'root',
        children: [
            {id: 1, text: 'A leaf Node', leaf: true},
            {id: 2, text: 'A folder Node', children: [
                {id: 3, text: 'A child Node', leaf: true}]
            }
        ]
    });
    var tree = new Ext.tree.TreePanel({
        el: 't', //要有個div的id為't'
        width: 150,
        autoHeight: true,
        root: root
    });
    tree.render();
});

記得如果是樹葉,記得要設定leaf:true,否則會認為不是leaf所以一直處於讀取狀態。

expandPath( String path, [String attr], [Function callback] ) : void
展開TreePanel中特定的路徑,比如我們要展開id2的id3:
Ext.onReady(function() {
    var root = new Ext.tree.AsyncTreeNode({
        id: 'aRoot',
        text: 'root',
        children: [
            {id: 1, text: 'A leaf Node', leaf: true},
            {id: 2, text: 'A folder Node', children: [
                {id: 3, text: 'A child Node', leaf: true}]
            }
        ]
    });
    var tree = new Ext.tree.TreePanel({
        el: 't', //要有個div的id為't'
        width: 150,
        autoHeight: true,
        root: root
    });
    tree.render();
    tree.expandPath('/aRoot/2/3');
});

selectPath( String path, [String attr], [Function callback] ) : void
選擇特定路徑上的node,用法和expandPath相同。
Ext.onReady(function() {
    var root = new Ext.tree.AsyncTreeNode({
        id: 'aRoot',
        text: 'root',
        children: [
            {id: 1, text: 'A leaf Node', leaf: true},
            {id: 2, text: 'A folder Node', children: [
                {id: 3, text: 'A child Node', leaf: true}]
            }
        ]
    });
    var tree = new Ext.tree.TreePanel({
        el: 't', //要有個div的id為't'
        width: 150,
        autoHeight: true,
        root: root
    });
    tree.render();
    tree.selectPath('/aRoot/2/3');
});



除了上述的建構方式,Ext.tree.TreeLoader也提供透過URL讀取JSON file來建構tree。JSON file(tree.json)如下:
[{
    id: 1,
    text: 'A leaf Node',
    leaf: true
  },{
    id: 2,
    text: 'A folder Node',
    children: [{
      id: 3,
      text: 'A child Node',
      leaf: true
    }]
  }]

leaf屬性會在load的時候,判斷是否要在遞迴產生子節點。而原本的Ext.tree.TreeNode並不支援Ajax,所以也要改成Ext.tree.AsyncTreeNode.程式碼如下。
Ext.onReady(function() {
    var root = new Ext.tree.AsyncTreeNode({text: 'root'});
    var tree = new Ext.tree.TreePanel({
        el: 't',
        width: 150,
        autoHeight: true,
        root: root,
        loader: new Ext.tree.TreeLoader({dataUrl: 'tree.json'})
    });
    tree.render();
});


2009年8月3日 星期一

台中 - 日式料理 - 潤和壽司店 ☆☆☆


這家店位於台中縣沙鹿鎮一個不起眼的小路上,是一個偶然的機會到這家店消費,吃完之後發現這是一間物美價廉非常值得一吃的店家。
這是店裡的價目表,東西真的是物超所值,吃過握壽司與炒烏龍,強力推薦,還有每日一物也要點唷,超級划算。
我點的經濟餐,每一個握壽司都是滿滿的料,東西超好吃,最愛煙燻鮭魚,全部只要$200,如果人多或是食量大,建議點豪華餐。
今天的每日一物是鮭魚沙拉壽司,這兩個只要$10,不點可惜,保證物超所值。
味增湯,湯頭也是不錯,魚肉也是新鮮吃得到。
類別:日式料理 店名:潤和壽司店 地點:台中縣沙鹿鎮沙鹿鎮光華路371號 評論:☆☆☆ 物美價廉