2013年12月29日 星期日

ExtJS4 類別別名表 (Alias)

今天在 "Ext JS 4 First Look" 這本書最後面找到 "Ext JS 4 Versus Ext JS 3 Class Names" 的附錄, 列出 ExtJS4 類別的別名, 對於類別名稱的參照很有用, 所以特別記下來.

我們在查 Ext JS API 時, 常看到很多類別都有 alternateClassName 這項屬性, 這就是其別名, 亦即要繼承或建立實例時用全名或別名都可以, Ext JS 4 新的類別系統在創建過程中會自動判別. 這是 Ext JS 4 為了與 Ext JS 3 達成向後相容的安排, 以便舊系統升級時能夠 "減痛升級" (任何升級要無痛是不可能的). 別名通常都比較短, 所以我們這些懶骨頭都很愛用. 但是建議還是使用新類別系統之全名較好.

這張表其實有一個網站如下 (用 ExtJS 的 Data Grid 做的) :

http://loianegroner.com/extjs/examples/ext4-ext3-class-names/

注意, 某些類別在 Ext JS 3 中有一個以上的類別名稱, 因此可能在這張表中出現多次, 例如 Ext.tree.Panel 與 Ext.grid.Panel 等等.

2013年12月28日 星期六

ExtJS 4 測試 : each 與 forEach 迴圈測試

我在 "ExtJS 架構原理與應用實例大全" (博碩) 這本書上看到一張 ExtJS 的架構圖, 覺得很能表達此框架的結構, 因此重繪了一張如下 :


ExtJS 把 Javascript 原生的功能加以擴充, 其中迴圈功能就屬於上圖中的核心元件部份. 迴圈是寫程式常用的技巧, 通常用在拜訪陣列元素或物件屬性. ExtJS 在許多類別或物件上都提供了 each 或 forEach 方法, 於 API 中搜尋約有 24 個 :

http://docs.sencha.com/extjs/4.2.2/#!/api/Ext.tab.Panel

此處我們測試其中四個最常用的 :
  1. Ext.Array.each() 與 Ext.Array.forEach()
  2. Ext.Object.each()
  3. Ext.iterate()
首先測試 Ext.Array.each(), 此方法有一個別名 Ext.each(), 可以少打一個 Array. 其參數格式如下 , 前兩個參數為必要, 後兩個為備選. 第一個參數 iterable 傳入要拜訪的陣列, fn 是回呼函式, scope 為作用域 (預設 this), reverse 為布林值 (預設 false), true 表示逆向拜訪.

each(iterable, fn, [scope], [reverse])

而回呼函式呼叫結構如下 :

fn(item, index, arr)  

它會傳入三個參數, 其中 item 是目前所拜訪之陣列元素, index 是其索引, 而 arr 則為陣列本身, 這三個參數都是備選的, 通常應用時是傳入前兩個參數 (不傳入參數我們是要幹啥?).

我們設立一個陣列 color, 然後用 Ext.each() 來遍歷, 如下列範例 1 所示 :

測試範例 1 : http://mybidrobot.allalla.com/extjstest/each_1.htm [看原始碼]
 
      var color=["red","white","blue","yellow","green","pink","black"];
      var output="";
      Ext.Array.each(color,
                       function(item, index) {
                          output += "color[" + index + "]=" + item + "<br>";
                          }
                       );
      Ext.Msg.alert("訊息",output);


在上例中, 回呼函式沒有接收第三參數, 即拜訪之陣列本身, 其實我們也可以用第三參數來取得陣列元素, 如下列範例 2 所示 :

測試範例 2http://mybidrobot.allalla.com/extjstest/each_2.htm [看原始碼]

      var color=["red","white","blue","yellow","green","pink","black"];
      var output="";
      Ext.each(color,
                      function(item, index, arr) {
                         output += "color[" + index + "]=" + arr[index] + "<br>";
                         }
                      );
      Ext.Msg.alert("訊息",output);


範例 2 中我們只是將範例 1 的 item 改成 arr[index], 效果一樣.

下面來測試 each() 的第四參數 reverse, 如下列範例 3 所示 :

測試範例 3http://mybidrobot.allalla.com/extjstest/each_3.htm [看原始碼]

      var color=["red","white","blue","yellow","green","pink","black"];
      var output="";
      Ext.Array.each(color,
                               function(item, index) {
                                  output += "color[" + index + "]=" + item +
"<br>";
                                  },
                               this, true
                               );
      Ext.Msg.alert("訊息",output);



可見當 reverse 設為 true 時, 將會逆序從陣列尾端開始拜訪. 注意, scope 不能略去不填, 否則 true 會被當成 scope, 導致被認為沒有傳入 reverse 而仍然以預設之順序拜訪陣列.

事實上, 使用 Javascript 原生的 for 或 for in 語法也不會很麻煩呀, 如下範例 4-1 與 4-2 所示 :

測試範例 4-1http://mybidrobot.allalla.com/extjstest/each_4_1.htm [看原始碼]

      var color=["red","white","blue","yellow","green","pink","black"];
      var output="";
      for (var i=0; i<color.length; i++) {
            output += "color[" + i + "]=" + color[i] +
"<br>";
            };
      Ext.Msg.alert("訊息",output);


測試範例 4-2http://mybidrobot.allalla.com/extjstest/each_4_2.htm [看原始碼]

      var color=["red","white","blue","yellow","green","pink","black"];
      var output="";
      for (i in color) {
           output += "color[" + i + "]=" + color[i] +
"<br>";
           };
      Ext.Msg.alert("訊息",output);


在 ECMAScript  5 還定義了一個 Javascript 陣列的原生 forEach() 方法, 用法跟 Ext.Array.forEach 幾乎一樣 (誰抄誰?), 如下列範例 4-3 所示 :

測試範例 4-3http://mybidrobot.allalla.com/extjstest/each_4_3.htm [看原始碼]

      var color=["red","white","blue","yellow","green","pink","black"];
      var output="";
      color.forEach(function(item,index){
         output += "color[" + index + "]=" + color[index] + "<br>";
         });
      Ext.Msg.alert("訊息",output);

不過 Ext.each() 還是有比較特殊的地方啦, 例如第二個參數回呼函式 fn 是有傳回值的 (布林值, 預設傳回 true), 如果傳回 false 會讓迴圈停止, 例如下列範例 5 所示 :

測試範例 5http://mybidrobot.allalla.com/extjstest/each_5.htm [看原始碼]

      var color=["red","white","blue","yellow","green","pink","black"];
      var output="";
      Ext.each(color,
                       function(item, index) {
                          if (item=="green") {return false;}
                          output += "color[" + index + "]=" + item + "<br>";
                          }
                       );
      Ext.Msg.alert("訊息",output);


可見當拜訪至索引 4 green 時, 便傳回 false 而中斷迴圈執行了.

接著要測試 Ext.Array 提供的另一個拜訪陣列的方法 forEach(). 其呼叫格式如下 (回呼函式 fn 與 each 相同, 只是傳回值不會影響迴圈執行) :

forEach(array, fn, [scope])

此方法用途與上面的 each() 相同, 但功能與效能上有些差異; 據 API 描述, forEach() 是委派給 Javascript 原生的 Array.prorotype.forEach() 來執行, 因此效能比 each() 要好. 其次, 功能上的差異是, forEach() 不能設定逆序拜訪, 也不能透過回呼函式傳回 false 來中斷迴圈.

下面範例 6 是改自範例 1, 只是將 each 改為 forEach 而已 :

測試範例 6http://mybidrobot.allalla.com/extjstest/each_6.htm [看原始碼]

      var color=["red","white","blue","yellow","green","pink","black"];
      var output="";
      Ext.Array.forEach(color,
                                        function(item, index) {
                                           output += "color[" + index + "]=" + item + "<br>";
                                           }
                                        );
      Ext.Msg.alert("訊息",output);

結果是一樣的, 但是要注意, 這裡必須寫 Ext.Array.forEach 全名 (因為它沒有 Ext.forEach 這樣的別名), 否則會出現找不到物件之錯誤.

下面要測試物件的拜訪方法 : Ext.Object.each(), 其呼叫結構如下 :

each(object, fn, [scope])

其第一參數為要拜訪之物件, 第二參數 fn 為回呼函式, 其呼叫結構如下 :

fn(key, value, object)

這三個參數都非必要, 但通常都要傳入前兩個 (不傳進來那能幹啥?). 如下列範例 7 所示 :

測試範例 7http://mybidrobot.allalla.com/extjstest/each_7.htm [看原始碼]

      var country={name:"伊朗",capital:"德黑蘭",code:"+98"};
      var output="";
      Ext.Object.each(country,
                                   function(key, value) {
                                      output += "country." + key + "=" + value + 
"<br>";
                                      }
                                  );
      Ext.Msg.alert("訊息",output); 



跟 Ext.Array.each() 一樣, 物件的 each 也可以在回呼函式中傳回 false 來中斷物件之拜訪, 如下列範例 8 所示 :

測試範例 8http://mybidrobot.allalla.com/extjstest/each_8.htm [看原始碼]

      var country={name:"伊朗",capital:"德黑蘭",code:"+98"};
      var output="";
      Ext.Object.each(country,
                                   function(key, value, object) {
                                      if (key=="code") {return false;}
                                          output += "country." + key + "=" + value +
"<br>";
                                          }
                                   );
      Ext.Msg.alert("訊息",output);

 

可見拜訪至國碼屬性 code 時就傳回 false 而停止迴圈了.

其實, 陣列是物件的一種, 只是其 key 為數值罷了. 因此我們也可以將陣列傳入 Ext.Object.each() 中遍歷, 如下列範例 9 所示 :

測試範例 9 : http://mybidrobot.allalla.com/extjstest/each_9.htm [看原始碼]

      var color=["red","white","blue","yellow","green","pink","black"];
      var output=""; 
      Ext.Object.each(color, 
                                 function(key, value, object) {
                                    if (key=="code") {return false;}
                                    output += "color." + key + "=" + value + "<br>";
                                    }
                               );
      Ext.Msg.alert("訊息",output);



可見陣列當物件看時, 其 key 就是其索引. 

如果物件有方法呢? 用 each() 遍歷時, 其值就是代表方法定義的函式內容, 如下列範例 10 所示 :

測試範例 10 : http://mybidrobot.allalla.com/extjstest/each_10.htm [看原始碼]

      function user(account,password) {
        this.account=account;
        this.password=password;
        this.changePassword=changePassword;
        function changePassword(newPassword) {
          this.password=newPassword;
          } 
        } 
      var user1=new user("tony","123");
      var output=""; 
      Ext.Object.each(user1, 
                                 function(key, value, object) {
                                    output += "user1." + key + "=" + value + "<br>";
                                    }
                                 );
      Ext.Msg.alert("訊息",output);



在上例中, 我們定義了具有一個方法的物件 user, 當遍歷物件時, 方法的內容會被當作 value 顯示出來.

上面範例 9 我們用 Ext.Object.each() 來拜訪陣列, 如果反過來呢? 也就是用 Ext.Array.each() 來拜訪物件呢? 這是行不通的, 整個物件會被當作只有一個元素的陣列, 如下範例 11 所示 :

測試範例 11 : http://mybidrobot.allalla.com/extjstest/each_11.htm [看原始碼]

      var country={name:"伊朗",capital:"德黑蘭",code:"+98"};
      var output=""; 
      Ext.Array.each(country, 
                                function(item, index) {
                                   output += "country[" + index + "]=" + item + "<br>";
                                  }
                              );
      Ext.Msg.alert("訊息",output);



所以, 陣列可以當物件來拜訪, 但是物件卻不能當物件來拜訪.

其實 ExtJS4 的根名稱空間 Ext 有一個 Ext.iterate() 方法是可以傳入陣列或物件的. 其參數格式與 Ext.Object.each() 相同, 從源碼可知, 其實它會先判斷傳入的是陣列還是物件, 若是陣列就呼叫 Ext.Array.each(); 否則就呼叫 Ext.Object.each(). 下列範例 11-1 是用 iterate 來拜訪陣列 :

測試範例 11-1 : http://mybidrobot.allalla.com/extjstest/each_11_1.htm [看原始碼]

      var color=["red","white","blue","yellow","green","pink","black"];
      var output="";
      Ext.iterate(color,
                       function(key, value) {
                          output += "color.'" + key + "=" + value + "<br>";
                          }
                       );
      Ext.Msg.alert("訊息",output);

範例 11-2 是用 iterate 來拜訪物件 :

測試範例 11-2 : http://mybidrobot.allalla.com/extjstest/each_11_2.htm [看原始碼]

      var country={name:"伊朗",capital:"德黑蘭",code:"+98"};
      var output=""; 
      Ext.iterate(country, 
                        function(key, value) {
                           output += "country." + key + "=" + value + "<br>";
                           }
                       );
      Ext.Msg.alert("訊息",output);

最後我們來比較一下各種迴圈的效能, 如下列範例 12 所示 (請用 Chrome 瀏覽, 按 Ctrl+Shift+J 即可打開下方控制台看到結果, Firefox 則按 F12) :

測試範例 12 : http://mybidrobot.allalla.com/extjstest/each_12.htm [看原始碼]

      //製作 100000 個元素的陣列
      var arr=[];
      for (var i=0; i<100000; i++) {arr[i]=i + 1;}
      //使用 Ext.Array.each
      var sum=0;
      console.time("Ext.Array.each");
      Ext.Array.each(arr, function(item, index) {sum += item;});
      console.timeEnd("Ext.Array.each");
      //使用 Ext.Array.forEach
      sum=0;
      console.time("Ext.Array.forEach");
      Ext.Array.forEach(arr, function(item, index) {sum += item;});
      console.timeEnd("Ext.Array.forEach");
      //使用 Ext.Object.each 迴圈
      sum=0;
      console.time("Ext.Object.each");
      Ext.Object.each(arr,function(key, value) {sum += value;});
      console.timeEnd("Ext.Object.each");
      //使用 Ext.iterate 迴圈
      sum=0;
      console.time("Ext.iterate");
      Ext.iterate(arr,function(key, value) {sum += value;});
      console.timeEnd("Ext.iterate");
      //使用 for 迴圈
      sum=0;
      console.time("for");
      for (var i=0; i<100000; i++) {sum += arr[i];}
      console.timeEnd("for");
      //使用 for in 迴圈
      sum=0;
      console.time("forin");
      for (item in arr) {sum += item;}
      console.timeEnd("forin");
      //使用 forEach 迴圈
      sum=0;
      console.time("forEach");
      arr.forEach(function(item, index) {sum += item;});
      console.timeEnd("forEach");




此處我們利用瀏覽器的 console.time() 與 console.timeEnd() 方法來計算迴圈所耗費的時間 (注意所傳入之參數起訖必須一致才能正確計算時間), 每次執行所得數據都會稍有變化, 但基本上差異不大. 由上可知這四種迴圈的效能依序是 :

第一名=for 
第二名=forEach, Ext.iterate, Ext.Array.forEach
第三名=for in
第四名=Ext.Object.each

可見 Javascript 原生的 for 效能還是最棒的, 但同樣原生的 for in 卻很遜, 所以最好少用. 其次, Ext.Array.forEach 委由 原生的 Array.prorotype.forEach 來執行, 效能確實比 each 要稍好, 雖然差距不大, 但迴圈數多時就很可觀了.

2013年12月27日 星期五

不是時間太少

總是抱怨忙得沒時間, 其實不是那麼回事, 是時間利用效率太差. 昨天早上寫了一張座右銘小紙條墊在公司桌面玻璃下面提醒自己 :

"不是時間太少, 而是浪費的太多"

具體作法如下 :

  1. 刪除瀏覽器工具列上的 Yahoo 連結
    看 Yahoo 新聞浪費太多時間, 尤其是政治版, 看了之後通常幹聲連連, 忍不住又要評論一番. 請問有啥鳥用嗎? 又沒本事鬧革命, 這麼多意見幹啥? 浪費時間.
  2. 辦公室保持靜默
    是非只因多開口, 除非公事詢問, 不要輕易開尊口~~~zip! 閒聊與議論於事無補, 反而耗費真氣, 浪費時間. 
  3. 寫下待辦清單
    有哪些計畫要執行, 早上 8 點到班後馬上做, 根據急重輕緩順序執行. 10 點後再收信處理郵件, 兩個小時可以做不少事.

常常看一眼上面的座右銘, 或許 ExtJS 兩個月就攻克了, 專案也做完了. 偉大的戰役通常是靠急行軍, 不是好整以暇的精密布局.


2013年12月23日 星期一

運動會補假

上週六菁菁學校 15 周年運動會, 所以今天補假, 我也剛好請國內旅行休一天, 早上在家把腳踏車小紅的鏽斑清理乾淨, 也把小金牽去補前輪 (內外胎 $600), 加上買一個換補的坐墊 $300, 一共花了 900 元, 厚, 腳踏車維修都不便宜啊!

這次運動會我本來要參加最後一項的家長大隊接力, 結果晚了一步, 我只是綁個鞋帶耽擱一下就額滿了, 殘念 ~~~.

所謂國內旅遊, 其實就是到澄清湖會館住一晚而已, 去年是菁菁二哥跟我去, 今年換姊姊與菁菁跟我去, 姊姊似乎很期待哩. 帶了四包泡麵去卻沒有時間泡 (姊姊在看王子的約會).

第二天 checkout 回鄉下前, 從澄清湖後門進去逛逛, 現在市民進去只要出示身分證即可.

菁菁在澄清湖爬樹


重回幼稚園

上周四本來要帶菁菁去科見, 阿妹突然打電話要我去幼稚園幫她接小朋友, 因為塞車無法在七點前到幼稚園, 我馬上開車去接. 已經五年沒回到幼稚園了 (菁菁五年級啦), 感覺路有點生疏, 但環境似乎沒變.

我先到蜻蜓班, 看見毛老師與大郭老師, 我說怎麼大家都沒變, 毛老師聽了笑哈哈. 真的很懷念那段接送時光, 如果不趕時間回家, 還可以跟小朋友比賽爬竿, 或踢一下足球. 孩子的幼稚園階段是人生中最美好的時光.

無奇不有

今天在 Youtube 上看到 "台灣瀕死經驗揭秘(1/3):金門朱秀華之借屍還魂!", 其實這個發生在雲林麥寮的奇異故事我很早以前就在書裡看過了, 當時非常驚訝, 覺得這世界真是無奇不有, 如果這是真的, 那人類所知的實在太渺小了, 呆記真的不是憨人想的這麼簡單啊.

片中訪問了精神科醫生的見解, 說明這是 "解離症" 的一種,  嘿嘿, 有時候會覺得西醫最厲害的一招就是 "術語發明術", 就是對某種現象無法解釋時, 就創造一個名詞來代替解釋 (稻草人戰術), 比如過敏, 當無法解釋為什麼會出現這種症狀時, 醫生就會說, 這可能是對什麼東東過敏.


2013年12月19日 星期四

看見台灣

下午請半天假參加菁菁五年八班的戶外教學 (還有其他班), 到高醫對面的影城去看齊柏林的紀錄片 "看見未來", 五年級其實已經沒有 (家長可以參加的) 戶外教學 (也沒有晨光教學), 所以這可能是我參加戶外教學的最後機會, 加上年底假沒休完會被同事訕笑 (最佳貢獻獎), 故特別要菁菁跟老師問, 家長可以參加嗎? 直到週二才確定還有名額.


老實說, 這種環保主題的片子這些小朋友不會覺得好看, 所以相信有不少人在電影院睡著了. 我自己覺得有點沉重, 雖然從高空看到美麗的嘉明湖與綠野平疇的稻田, 身為農家子弟的我有一股悸動, 但是想到那人與天爭, 人與地爭的無奈, 就會覺得這都是一種宿命, 無法逆轉, 即使知道這將使後代子孫無淨水, 無良土, 也只能眼睜睜的看他如預言那樣發生. 就好像我為了減少回收垃圾, 勤於煮飯, 但是每次去倒垃圾時, 看到街坊鄰居要回收的一大袋免洗餐具, 我就想, 我的努力算啥?

很無力, 雖然不願意, 但醜話還是得說, 我想, 人類現在對環保的努力可能已經太晚了, 資本主義已經將人類帶上不歸路, 地球均溫的上昇, 物種驚人速度的滅絕, 其實已經在為人類將來的大滅絕鋪路了. 要經濟不再成長, 那是不可能的.

片中提到後龍那位提倡有機種植的范女士, 她說, "蟲子吃剩下的, 才是我們的, 我們需要的其實不多, 但想要的太多". 良哉斯言, 但能認同的有多少? 即使感動, 那可能只是一時的. 就像江宜樺看過此片後, 掀起了清境民宿拆除問題, 我想這也是一時的, 在政治力運作之下, 很快就會靜下來的.

這一切的根源都在於, 人類真的太多了.

# PS :
一路上都有小朋友跟我打招呼, 這些都是菁菁三四年級的同學, 雖然不同班了, 還是認得我, 哈哈, 這種快樂就是以前晨光教學的最大收穫ㄚ.


2013年12月14日 星期六

宗教使人生更美好?

這幾天新聞頭版的魔教 "日月明功" 害死了一個可憐的高中生, 讓我這個從小就開始研究宗教的人 (國中就接觸基督教), 對宗教的反思更深刻, 也更 ~~~ 反動. 看看這篇新聞, 那句 "媽媽救我" 聽了令我心痛.

# 高中生遭虐殺 死前哭喊:媽媽救我


這幾個大人沒讀書嗎? 學歷不高麼? 為什麼在教育如此高水準的時代, 還會有這種事? 因為科學發達並不能解決人的心理問題, 靈魂與物質在凡人來講, 永遠是兩回事. 心靈貧乏才會尋找依靠, 而神棍正是抓住這個弱點, 建立權威, 要信徒怎樣就怎樣, 不但財源滾滾, 邪教騙財騙色時有耳聞. 難怪這年頭開廟比開公司賺, 又不用煩惱 EPS.

學生時代我接觸過基督教, 摩門教, 一貫道, 佛教, 獨缺回教 (在台北時曾想去清真寺體會一下). 這些是所謂的名門正教 (一貫道早年因秘密傳教與政治因素, 被國民黨定位為邪教), 但我總在想, 宗教的存在是讓人生變美好, 還是更悽慘?

如果更美好, 那同為耶教的北愛, 為什麼以前常常發生新教與天主教徒互相開火, 公車爆炸案層出不窮 (現在似乎好一些了) ? 難道他們的上帝有所不同? 聖經版本有異? 為什麼回教激進派會認為消滅異教徒是一場聖戰, 自殺攻擊會上天堂? 甚至現在連最少與異教衝突的佛教, 在緬甸也發生佛教與回教徒村鎮之間的宗教仇殺? 還有, 理應聖潔的天主教神父與據稱已開悟的仁波切, 為何傳出這麼多性侵誹聞?

這一切, 都在告訴我們一件事實, 別傻了, 我們全部都是人, 不是神. 任何一個昭告世界說他已是神佛的, 除非他沒有形體, 否則, 應該都還是人, 或者, 獸.

愚以為, 宗教之所以成為禍害, 原因有三個 :
  1. 排他性 :
    就是唯一真神啦. 我信仰的神是真神, 那異教的當然是假神, 當然要打倒. 所以才會有聖戰. 信我者得永生, 不信者就下地獄, 這是號稱博愛的上帝嗎? 請問那些滿天都是神的希臘, 埃及, 佛道教, 愛斯基摩人, 會有什麼聖戰? 你跟他傳教, 可能只是讓他們的世界又多了一尊神而已.
  2. 怪力亂神 :
    製造假的神通來造神, 用來愚弄因為感情, 事業, 疾病等人生不幸而上門求助者. 神通或許是有的, 但真正擁有的都是高人, 早已不是需要用這個來炫耀的等級了 (這些人都很怕造惡因).
  3. 權柄 :
    在世俗無法獲得權力者 (當不成總統, 董事長), 在宗教中很輕易就可以享有. 只要有了山頭 (就是蓋大廟), 有了徒眾, 權威就有了, 那種權柄比世俗還要大上不知幾倍, 因為世俗的權柄是靠槍桿子, 靠法律才擁有, 但宗教大師的權柄卻是靠信徒的敬畏與景仰, 連總統跟董事長都要匍地作揖, 你說誰的權柄大? 權有了, 錢那就不是問題了.
宗教的唯一用途只有一個, 那就是帶給人心靈的合諧 (harmony). 人生一定會歷經波折, 對人生感到徬徨時, 在無可奈何之餘能夠在心裡有個依托, 最後在離開這世間時不致感到恐懼. 所以, 不論信何教, 如果能讓人心獲得 harmony, 那就是你需要的. 至於信哪一個教才能得救贖, 我覺得, 說這些話的本身都還沒得救贖. 還沒中樂透的人, 不要跟別人描述中獎是何等的喜悅. 悟道跟中樂透其實是一樣的, 那種滋味只有自己中了才知道, 那些說得口若懸河的, 喊燒而已.

2013年12月11日 星期三

可惡的 Snap.DO

我在二哥的小電腦安裝最新版 Picpick 後, Chrome 瀏覽器在 Win7 工作列上的圖標被改成一個放大鏡, Chrome 的預設網址也從 Google 被改成 snap.do 那討人厭的怪蟲. 這就是惡名昭彰的瀏覽器綁架者, 即使在設定中刪除, 下次開機又回來了, 真是陰魂不散. 晚上搜尋到下面兩篇文章, 照方對治, 終於幹掉 Snap.do 了.

其實上回安裝 KMplayer 時也會問你要不要安裝某 tool bar 之類的, 如果沒仔細看, 一直按確定, 就會安裝一些可能有安全危害的軟體.

Picpick 抓圖編輯功能很不錯, 但是不要下載新版的安裝, 請下載很舊版的免安裝版, 然後設定成隨系統啟動, 這樣就會常駐右下角快捷區了.

如何完整移除Snap.do (及類似網站) 
移除煩人的snap.do

賤人就是矯情

同學傳給我一個笑話 :


2013年12月10日 星期二

新上15

昨天陪菁菁上學時,看見東北門校舍牆上貼上大幅壁畫,回程時細看,原來菁菁有在上面耶!  新上國小已經15歲了,Happy birthday!


2013年12月9日 星期一

鞭蠍

週日在鄉下書房看到一隻很像蠍子的蟲,仔細一看,原來是俗名酸蟲的鞭蠍。當它被驚擾時, 會噴出醋酸, 會聞到很酸的味道, 故名酸蟲. 據維基百科說, 這蟲雖名蠍, 但無毒, 而且還是卵胎生呢. 我用廢紙把它捲起來丟到外面曬穀場讓它自行逃生去也.

還有一種奇怪而且很討厭的蟲, 叫做衣魚, 就是抽屜裡常見的書蟲啦, 專門吃書, 它後面那兩根晃而晃的尾巴令人看了尤其生厭. 最近整理鄉下書房久已不用的書桌, 裡面就一大堆衣魚, 所以本週特地買了樟腦丸來制它.

 

La New 健行

週六 12/7 帶菁菁搭捷運去世運主館參加 La New 萬人大健行,現場人擠人,拖到九點半才出發,本來還在想幹嘛跑這麼遠來健行,在河堤公園走不是一樣嗎?
後來想,既來之則安之,反正平常也沒機會來這邊。健行路線是經左營軍區門口,繞進眷村 (好像很多戶都破舊沒人住了),再到蓮池潭繞一圈,然後走回世運主館,全長八公里。
沒走真的感覺不到原來蓮池潭這麼大,加上天氣熱,菁菁剛出發沒多久就開始哇哇叫,但還是走完全程。

 

2013年12月8日 星期日

ExtJS 4 測試 : 頁籤面板 TabPanel

頁籤面板簡單好用, 直觀明瞭, 是我寫專案最愛用的的 UI 容器. ExtJS 的頁籤面板美觀易用, 由 Ext.tab.Panel 類別提供 (別名 Ext.TabPanel), 它是 Ext.panel.Panel 的子類別, 每一個頁籤都是一個 Ext.panel.Panel 物件, 同一時間只有其中一個是作用中頁籤 (active tab), 當點選其他頁籤時, 該頁籤就變成作用中頁籤了.

TabPanel 類別事實上乃一特殊化的 Panel 類別, 由標籤列與內容主體兩部分構成, 標籤列 (TabBar 物件) 事實上就是一組按鈕, 而內容主體則是一個 CardLayout 容器物件, 用來顯示與標籤對應之 HTML 內容. 標籤列預設位置是在上方, 透過設定可以改為下方或左右位置的標籤列. 此 TabPanel 類別的 API 文件詳見 :

http://docs.sencha.com/extjs/4.2.2/#!/api/Ext.tab.Panel

以下就根據 API 文件來測試頁籤面板的常用功能. 要建立一個頁籤面板只要繼承 Ext.tab.Panel 類別, 並設定所需之 items 屬性即可, ExtJS 會自動在標籤切換時更換主體內容. 如下列範例 1 所示 :

測試範例 1 : http://mybidrobot.allalla.com/extjstest/tabpanel_1.htm [看原始碼]

      Ext.create("Ext.tab.Panel",{
        title:"工作日誌系統",
        items:[
          {title:"首頁",html:"首頁"},
          {title:"差勤",html:"差勤"},
          {title:"管理",html:"管理"}
          ],
        renderTo:Ext.getBody()
        });

此 處 renderTo 設為 Ext.getBody(), 此方法會傳回 body 元素 Ext.Element 物件, 亦即將頁籤面板直接描繪於 body 文件中. 當然若要繪製於文件的內部元素也是可以的, 這時 renderTo 就要設定為該元素的 id 字串, 如下面範例 1-1 所示 :

測試範例 1-1 http://mybidrobot.allalla.com/extjstest/tabpanel_1_1.htm [看原始碼]

  <div id="tabpanel" style="margin:15px;"></div>
  <script type="text/javascript">
    Ext.onReady(function() {
      Ext.create("Ext.tab.Panel",{
        title:"工作日誌系統",
        items:[
          {title:"首頁",html:"首頁"},
          {title:"差勤",html:"差勤"},
          {title:"管理",html:"管理"}
          ],
        renderTo:"tabpanel"
        });
      });
  </script>

從上例可知, 預設情況下, 標籤列是在上方, 寬度占據整個頁面, 高度則依頁籤內容而定. 如果要將標籤列放在下方, 就要將 tabPosition 屬性設為 "bottom". 同時也可以分別用 width 與 height 屬性設定寬度與高度, 如範例 2 所示 :


      Ext.create("Ext.tab.Panel",{
        title:"工作日誌系統",
        width:300,
        height:100,
        tabPosition:"bottom",
        items:[
          {title:"首頁",html:"首頁"},
          {title:"差勤",html:"差勤"},
          {title:"管理",html:"管理"}
          ],
renderTo:Ext.getBody()
        });






 

測試範例 2 : http://mybidrobot.allalla.com/extjstest/tabpanel_2.htm [看原始碼]

我們也可以將 tabPosition 改為 "left" 或 "right", 如下面範例 3 為左方標籤列 :

測試範例 3 : http://mybidrobot.allalla.com/extjstest/tabpanel_3.htm [看原始碼]



在範例 3 中, 我們把標籤改在左邊, 可見標籤文字都變成直排, 但要轉過來看, 不見得實用. 

接下來要測試兩個屬性 : closable (可關閉) 與 disabled (禁用), 這兩個屬性都是 true/false 布林值, 被設為 disable: true 的標籤將無法點選 (灰色), 而被設為 closable:true 的標籤, 其右邊會有一個 X, 按下去會關閉該標籤. 

測試範例 4 : http://mybidrobot.allalla.com/extjstest/tabpanel_4.htm [看原始碼]

      Ext.create("Ext.TabPanel",{
        title:"工作日誌系統",
        width:300,
        height:200,
        items:[
          {title:"首頁",html:"首頁",closable:true},
          {title:"差勤",html:"差勤",closable:true},
          {title:"管理",html:"管理",disabled:true}
          ],
        renderTo:Ext.getBody()
        });


在上例中, 我們在 head 裡設定了 body 的樣式, 讓整個頁籤與瀏覽器邊框有 10px 的距離 :

  <style>
    body {padding:10px;}
  </style>

其實這可以用 style 屬性來設定, 如下列範例 5 所示 :

測試範例 5 : http://mybidrobot.allalla.com/extjstest/tabpanel_5.htm [看原始碼]

      Ext.create("Ext.TabPanel",{
        title:"工作日誌系統",
        width:300,
        height:200,
        style:"padding:10px;background-color:yellow;",
        items:[
          {title:"首頁",html:"首頁",closable:true},
          {title:"差勤",html:"差勤",closable:true},
          {title:"管理",html:"管理",disabled:true}
          ],
        renderTo:Ext.getBody()
        });


在上例中, 我們去除 head 中的 body {padding:10px} 樣式, 轉而在 TabPanel 物件中設定了 style 屬性, 還加上了 background-color 樣式. 

在以上範例中, 頁籤主體內容裡的文字緊貼著邊, 在視覺上不太協調, 其實這可以在每個頁籤項目中用 bodyPadding 屬性來設定內容與邊框之距離. 在下列範例 6 中, 同時也將 TabPanel 的 frame 屬性設為 true, 這樣頁籤主體內容外就會有一個邊框 :

測試範例 6 : http://mybidrobot.allalla.com/extjstest/tabpanel_6.htm [看原始碼]

      Ext.create("Ext.TabPanel",{
        title:"工作日誌系統",
        width:300,
        height:200,
        frame:true,
        items:[
          {title:"首頁",html:"首頁",bodyPadding:10},
          {title:"差勤",html:"差勤",bodyPadding:10},
          {title:"管理",html:"管理"}
          ],
        renderTo:Ext.getBody()
        }); 



在上例中可見頁籤內容外有約 2px 的邊框出現, 且第一個與第二個頁籤之文字內容與邊框之間也有 10px 的空間, 而第三個頁籤則無. 但是如果每一個頁籤都要這樣設 bodyPadding 屬性很麻煩, 這可以用 defaults 屬性作一次設定 :

測試範例 7 : http://mybidrobot.allalla.com/extjstest/tabpanel_7.htm [看原始碼]

      Ext.create("Ext.TabPanel",{
        title:"工作日誌系統",
        width:300,
        height:200,
        frame:true,
        defaults:{bodyPadding:10, closable:true},
        items:[
          {title:"首頁",html:"首頁"},
          {title:"差勤",html:"差勤"},
          {title:"管理",html:"管理"}
          ],
        renderTo:Ext.getBody()
        });  .

上例中每一個頁籤 (items) 都會有一個 bodyPadding:10 的與 closable:true 屬性值, 但不用在 items 中一一設定, 其實只要是每一個頁籤都一樣的屬性值, 都可以全部統一於 defaults 中設定 (就好像是乘法的分配率一樣). 



前面兩例中的 bodyPadding 屬性只能設定內容與邊框間之留白, 如果要設定內容區的其他樣式該怎麼做? 其實有一個更一般的屬性 bodyStyle 可用, 如下面範例 7-1 所示 :

測試範例 7-1 : http://mybidrobot.allalla.com/extjstest/tabpanel_7_1.htm [看原始碼]

      Ext.create("Ext.TabPanel",{
        title:"工作日誌系統",
        width:300,
        height:200,
        frame:true,
        defaults:{bodyStyle:"padding:15px;background-color:yellow;"},
        items:[
          {title:"首頁",html:"首頁"},
          {title:"差勤",html:"差勤"},
          {title:"管理",html:"管理"}
          ],
        renderTo:Ext.getBody()
        }); 



但是, 並非每一個樣式都能生效, 例如 height 就不行, 如下列範例 7-2 所示 :


測試範例 7-2http://mybidrobot.allalla.com/extjstest/tabpanel_7_2.htm [看原始碼]

      Ext.create("Ext.TabPanel",{
        title:"工作日誌系統",
        width:300,
        frame:true,
        defaults:{bodyStyle:"width:100%;height:100%;background-color:yellow;"},
        items:[
          {title:"首頁",html:"首頁"},
          {title:"差勤",html:"差勤"},
          {title:"管理",html:"管理"}
          ],
        renderTo:Ext.getBody()
        }); 


我們刻意把整個 TabPanel 的 height 屬性拿掉, 然後在 bodyStyle 裡加入 height:100%, 希望頁籤的主體高度可以塞滿整個瀏覽器, 但是這樣設定是無效的. 而 bodyStyle 中的 width:100% 是有效的, 但是若同時有設定TabPanel 的 width 屬性的話, bodyStyle 中的 width 會被忽略. 注意, TabPanel 的 width 與 height 都是數值 (px), 所以不能將其設為 height:100%, 這樣會出現 "Unexpected token" 錯誤.

要讓頁籤面板高度填滿瀏覽器必須使用 Ext.container.Viewport 容器, 並且將 layout 設定為 "fit" 才行, 如下列範例 7-3 所示 :

測試範例 7-3http://mybidrobot.allalla.com/extjstest/tabpanel_7_3.htm [看原始碼]

      Ext.create("Ext.container.Viewport",{
        layout:"fit",
        items:{
          xtype:"tabpanel",

          title:"工作日誌系統",
          frame:true,
          defaults:{bodyPadding:10, closable:true},
          items:[{title:"首頁",html:"首頁"},
                     {title:"差勤",html:"差勤"},
                     {title:"管理",html:"管理"}]
          },
        renderTo:Ext.getBody()


此處我們使用 xtype 來建立頁籤面板, 並作為一個元件放進 Viewport 容器之中, Viewport 是一個能撐滿整個瀏覽器的容器, 而且當瀏覽器大小調整時, Viewport 內的元件也會隨同調整.

接下來我們來測試一下 plain 這個屬性, 用來設定是否要顯示頁籤列的背景圖, 它是一個布林值, 預設為 false, 如果設為 true, 頁籤列的背景圖就沒有了, 如下列範例 8 所示 :

測試範例 8 : http://mybidrobot.allalla.com/extjstest/tabpanel_8.htm [看原始碼]

      Ext.create("Ext.TabPanel",{
        title:"工作日誌系統",
        width:300,
        height:200,
        frame:true,
        plain:true,
        defaults:{bodyPadding:10},
        items:[
          {title:"首頁",html:"首頁"},
          {title:"差勤",html:"差勤"},
          {title:"管理",html:"管理"}
          ],
        renderTo:Ext.getBody()
        });



可見將 plain 設為 true 後, 標籤列後面那個方塊就不見了. 這通常是把頁籤面板放到其他容器, 作為其中一個組件時, 為了讓畫面不要那麼複雜才會用到, 故通常也不需要標題, 亦即 title 屬性也不需設定, 如下列範例 9 所示 :

測試範例 9 : http://mybidrobot.allalla.com/extjstest/tabpanel_9.htm [看原始碼]

      Ext.create("Ext.TabPanel",{
        width:300,
        height:200,
        frame:true,
        plain:true,
        defaults:{bodyPadding:10},
        items:[
          {title:"首頁",html:"首頁"},
          {title:"差勤",html:"差勤"},
          {title:"管理",html:"管理"}
          ],
        renderTo:Ext.getBody()
        });



上例中把 title 拿掉, 同時 plain 設為 true 畫面看起來就較為單純了.

以上在頁籤列項目中, 我們僅指定了 items 屬性的 title 與 html 兩個子屬性, 接下來我們要用到另一個屬性 tabConfig 來測試標籤列的提示功能. 前面我們已知頁籤列 TabBar 其實是按鈕組成的, 其文字通常很簡約, 能不能讓使用者滑鼠移到頁籤上就跳出一個說明文字呢? 可以的, ExtJS 有一個直接繼承自根類別 Ext.Base 的提示類別 Ext.tip.QuickTipManager 類別 (別名是 Ext.QuickTips, 注意是複數), 可以很方便為每個頁籤設定提示文字. 此類別用法詳見 API :

http://docs.sencha.com/extjs/4.2.2/#!/api/Ext.tip.QuickTipManager

下面範例 10 演示如何為頁籤加上提示 :

測試範例 10 : http://mybidrobot.allalla.com/extjstest/tabpanel_10.htm [看原始碼]

      Ext.tip.QuickTipManager.init();
      Ext.create("Ext.TabPanel",{
        title:"工作日誌系統",
        width:300,
        height:200,
        style:"margin:10px;",
        frame:true,
        items:[
          {title:"首頁",
           html:"首頁",
           tabConfig:{
             tooltip:"This ia Home!"
             }
           },
          {title:"差勤",
           html:"差勤",
           tabConfig:{
            tooltip:{title:"Attendance",text:"This is Attendance!"}
            }
           },
          {title:"管理",html:"管理"}
          ],
        renderTo:Ext.getBody()
        });



在上例中, 在使用 QuickTips 的提示功能之前, 一定要先呼叫其 init() 方法. 然後於每個頁籤項目中以 tabConfig 屬性設定 tooltip 子屬性. 此 tooltip 之值可以為字串, 就是要在頁籤按鈕上顯示的提示字串, 如上面左圖所示; 也可以是物件, 其中 title 是粗體的標題, text 是提示字串, 如上面右圖所示. 由於 Ext.tip.QuickTipManager 名字太長了, 它有一個別名 Ext.QuickTips, 所以上面程式碼中, 初始化也可以寫成 Ext.QuickTips.init(). 

其實頁籤物件的 items 屬性的 tabConfig 還可以放別的屬性, 例如在頁籤上放置圖形. 這要用到 iconCls 屬性, 如下範例 10-1 所示 :

測試範例 10-1 : http://mybidrobot.allalla.com/extjstest/tabpanel_10_1.htm [看原始碼]

首先要準備一個圖檔, 例如表示首頁的 home.gif, 然後在 style 中設定 Class :

    .icon-home {
       background-image: url('home.gif');
       background-repeat: no-repeat;
       }

然後在首頁的 item 中, 於 tabConfig 中添加 iconCls 屬性 :

      var tp=Ext.create("Ext.TabPanel",{
        title:"工作日誌系統",
        width:300,
        height:200,
        style:"margin:10px;",
        frame:true,
        defaults:{bodyPadding:10},
        items:[
          {title:"首頁",
           html:"首頁",
           tabConfig: {iconCls: 'icon-home'}
           },
          {title:"差勤",html:"差勤"},
          {title:"管理",html:"管理"}
          ],
        renderTo:Ext.getBody()
        });



如上圖所示, 在首頁頁籤左側出現了指定之圖示. 事實上這個圖示是可以在程式中動態變更的, 這要用到頁籤物件的 setIconCls 方法, 如下面範例 10-2 所示

測試範例 10-2http://mybidrobot.allalla.com/extjstest/tabpanel_10_2.htm [看原始碼]

為了動態更換圖示, 我們要再準備一個圖示 home-2.gif, 並設定其 class 危 icon-home-2 :

    .icon-home {
       background-image: url('home.gif');
       background-repeat: no-repeat;
       }
    .icon-home-2 {
       background-image: url('home-2.gif');
       background-repeat: no-repeat;
       }


然後添加一個 tbar 屬性, 這屬性來自 Ext.panel.Panel 類別, 預設元件類型 xtype 為 button :

       var tp=Ext.create("Ext.TabPanel",{
        title:"工作日誌系統",
        width:300,
        height:200,
        style:"margin:10px;",
        frame:true,
        defaults:{bodyPadding:10},
        tbar: [{
          text:"切換圖示",
          handler: function(){
            tp.items.get(0).tab.setIconCls('icon-home-2');
            }
          }],
        items:[
          {title:"首頁",
           html:"首頁",
           tabConfig: {iconCls: 'icon-home'},
           },
          {title:"差勤",html:"差勤"},
          {title:"管理",html:"管理"}
          ],
        renderTo:Ext.getBody()
        });


 
在上例中, 我們同時測試一下 tbar 屬性 (top bar), 此屬性會在頁籤列下面多一個工具列, 可以放置按鈕等元件. 此處我們放置了一個切換圖示鈕, 並設置其 click 事件的處理器, 變更首頁頁籤的圖示. 

也可以把頁籤的標題 title 放在 tabConfig 屬性中, 如果頁籤與 tabConfig 中同時都有設定 title 屬性, 這樣 tabConfig 的 title 將會覆蓋頁籤的 title (即使 tabConfig 先宣告也是一樣), 也可以用 width 設定頁籤按鈕的寬度, 如下面範例 10-3 所示 :

測試範例 10-3 : http://mybidrobot.allalla.com/extjstest/tabpanel_10_3.htm [看原始碼]

      Ext.create("Ext.TabPanel",{
        title:"工作日誌系統",
        width:300,
        height:200,
        style:"margin:10px;",
        frame:true,
        defaults:{bodyPadding:10},
        items:[
          {html:"首頁",tabConfig:{title:"Home",width:150},title:"首頁"},
          {title:"差勤",html:"差勤"},
          {html:"管理",tabConfig:{title:"管理"}}
          ],
        renderTo:Ext.getBody()
        });

上例中的首頁頁籤裡, tabConfig 的 title 屬性值覆蓋了頁籤物件本身的 title 屬性, 顯示 "Home"; 而管理頁籤中, 我們沒有設定頁籤物件的 title 屬性值, 而改在 tabConfig 物件中設定, 效果相同.

接著要測試 minTabWidth 屬性, 這是用來限制頁籤寬度的, 如下列範例 11 所示 :

測試範例 11 : http://mybidrobot.allalla.com/extjstest/tabpanel_11.htm [看原始碼]


      Ext.create("Ext.TabPanel",{
        title:"工作日誌系統",
        width:300,
        height:200,
        style:"margin:10px;",
        frame:true,
        minTabWidth:70,
        items:[
          {title:"首頁",html:"首頁"},
          {title:"差勤",html:"差勤"},
          {title:"例行檢查",html:"例行檢查"},
          {title:"管理",html:"管理"}
          ],
        renderTo:Ext.getBody()
        });



可見當設定 minTabWidth (單位 px) 後, 每一個頁籤寬度都至少為 70px 了, 如果沒設定的話, 預設頁籤寬度是自動調整到與文字同寬. 同時頁籤寬度變大會導致後面的頁籤看不到, ExtJS 會自動在左右兩邊出現前進後退按鈕來滑動頁籤.

也可以用 
maxTabWidth 屬性來限制頁籤的最大寬度, 如下面範例 12 所示 :

測試範例 12 : http://mybidrobot.allalla.com/extjstest/tabpanel_12.htm [看原始碼]

      Ext.create("Ext.TabPanel",{
        title:"工作日誌系統",
        width:300,
        height:200,
        style:"margin:10px;",
        frame:true,
        maxTabWidth:50,
        items:[
          {title:"首頁",html:"首頁"},
          {title:"差勤",html:"差勤"},
          {title:"例行檢查",html:"例行檢查"},
          {title:"管理",html:"管理"}
          ],
        renderTo:Ext.getBody()
        }); 



可見較長文字的頁籤會被刪節成 "xx ..." 以符合最大寬度之限制. 

上面範例中, 頁籤面板預設都是開啟第一個頁籤, 如果想要開啟特定頁籤該怎麼做? 這可以用 activeTab 屬性來設定 (索引從 0 起算). 另外, 當頁籤內容之長度超過 height 屬性值時, 超過部分是不會顯示的, TabPanel 預設不會在右邊出現滑動桿 (scroll bar), 這必須在項目中把 autoScroll 屬性設為 true 才行. 我們上面所有頁籤都是固定不動的, 如果要讓整個頁籤面板可以拖曳, 就要將 draggable 屬性設為 true. 以上三屬性測試如下列範例 13 所示 :

測試範例 13 : http://mybidrobot.allalla.com/extjstest/tabpanel_13.htm [看原始碼]

      var w="w<br>w<br>w<br>w<br>w<br>w<br>w<br>w<br>w<br>w<br>w<br>w";
      Ext.create("Ext.TabPanel",{
        title:"工作日誌系統",
        width:300,
        height:200,
        style:"margin:10px;",
        frame:true,
        draggable:true,
        activeTab:1,
        defaults:{bodyPadding:10},
        items:[
          {title:"首頁",html:w,autoScroll:true},
          {title:"差勤",html:w},
          {title:"管理",html:"管理"}
          ],
        renderTo:Ext.getBody()
        }); 


上例中網頁開啟時, 因為 activeTab 設為1 (注意, activeTab 的值為 0 起始的頁籤索引), 所以預設顯示的作用中 (active) 頁籤是第二頁, 而其內容長度高於 height 的 200px, 但是後面的 w 字元卻都無法顯示. 而右圖切到第一頁籤, 由於該項目設定了 autoScroll:true, 故可看到右方出現滑動桿, 可以看到完整的頁籤內容. 如果每一個頁籤內容都要在內容長度超過高度時自動出現滑動桿, 那麼可以把 autoScroll:true 放在 defaults 屬性中. 注意, autoScroll 是 items 物件的屬性, 不是 TabPanel 物件的屬性, 只能放在 defaults 中, 不要放錯位置.

上例是在建立 TabPanel 物件時靜態地指定作用中頁籤, 如果要在頁籤面板建立後用程式動態地去改變該怎麼做? 這就要用到 setActiveTab() 方法了. 在下面範例 14 中, 同時要用到 TabPanel 的另外一個好用的屬性 buttons, 可以在頁籤上添加按鈕來控制頁籤.

測試範例 14 : http://mybidrobot.allalla.com/extjstest/tabpanel_14.htm [看原始碼]

      var tp=Ext.create("Ext.TabPanel",{
        title:"工作日誌系統",
        width:300,
        height:200,
        style:"margin:10px;",
        frame:true,
        defaults:{bodyPadding:10},
        buttons:[{text:"新增",handler:addTab}],
        items:[
          {title:"首頁",html:"首頁"},
          {title:"差勤",html:"差勤"},
          {title:"管理",html:"管理"}
          ],
        renderTo:Ext.getBody()
        }); 
      function addTab() {
        var idx=tp.items.length + 1;
        var newItem={title:"tab" + idx,html:"tab" + idx};
        var newTab=tp.add(newItem);
        tp.setActiveTab(newTab);
        }


上例中, buttons 屬性所設定之按鈕預設會出現在頁籤面板下方 (bbar 物件) 的右邊, 其 click 事件處理函式會新增一個新的頁籤, 並且將新頁籤設為作用中 (active). 此處因為要操作頁籤, 因此要將建立的頁籤面板物件在 create 後傳回給變數 tp, 用 tp.items 便取得所有頁籤物件集合 (陣列), 其長度 tp.items.length 就是目前已有之頁籤數目, 加 1 後做為新頁籤索引以便製作頁籤項目, 再呼叫頁籤面板的 add 方法將此新項目加入面板中, 並呼叫 setActiveTab() 方法將新頁籤設為作用中.

上面的按鈕預設是在右方, 可以用 buttonAlign 屬性改為左方 (設為 "left") 或中間 ("center") 改為左方, 在下面範例 15 中, 我們同時要測試 TabPanel 的 remove() 方法, 也要用到 itemId 或 id 屬性.

測試範例 15 : http://mybidrobot.allalla.com/extjstest/tabpanel_15.htm [看原始碼]

      var tp=Ext.create("Ext.TabPanel",{
        title:"工作日誌系統",
        width:300,
        height:200,
        style:"margin:10px;",
        frame:true,
        defaults:{bodyPadding:10},
        buttons:[{text:"新增",handler:addTab},
                     {text:"刪除",handler:delTab}],
        buttonAlign:"left",
        items:[
          {title:"首頁",html:"首頁"},
          {title:"差勤",html:"差勤"},
          {title:"管理",html:"管理"}
          ],
        renderTo:Ext.getBody()
        });
      function addTab() {
        var idx=tp.items.length + 1;
        var newItem={title:"tab" + idx,itemId:"tab" + idx,html:"tab" + idx};
        var newTab=tp.add(newItem);
        tp.setActiveTab(newTab);
        }
      function delTab() {
        var idx=tp.items.length;
        var delTab=tp.remove("tab" + idx, true);
        }


在上例中, 我們在面板中放置了兩個按鈕, 因為 buttonAlign 設為 "left", 因此他們都靠左對齊了. 在 事件處理函式 addTab 中, 我們給新增的頁籤項目添加了 itemId 屬性, 因為在刪除新增頁籤時, 需要用此來識別. 當然用 id 也是可以的, 但建議使用 itemId, 因為當系統複雜時, 必須全域唯一的 id 可能會跟別的元件衝突而導致功能錯誤, 而用 itemId 則不會有此問題.

在範例 15 中, 按下刪除鈕會反序刪除新增的頁籤, 這是靠呼叫頁籤面板的 remove() 方法達成的, 它可以傳入兩個參數, 第一個是要刪除的元件 (id 或 itemId), 第二個是 autoDestroy (true/false), 這是選項. 當全部新增頁籤都被刪除只剩下原來 3 個頁籤時, 因為找不到 tab3, 所以就不會繼續刪除, 但也不會有 error.

注意, 存取 itemId 時要用頁籤面板物件呼叫 getComponent() 方法, 而存取 id 則是直接使用 Ext 類別的靜態方法 getCmp(), 如果顛倒了會出現錯誤. 下列範例 15-1 說明 id 與 itemId 之用法 :

測試範例 15-1 : http://mybidrobot.allalla.com/extjstest/tabpanel_15_1.htm [看原始碼]

      var tp=Ext.create("Ext.TabPanel",{
        title:"工作日誌系統",
        width:300,
        height:200,
        style:"margin:10px;",
        frame:true,
        defaults:{bodyPadding:10},
        buttons:[{text:"顯示首頁",handler:showHome},
                     {text:"顯示管理",handler:showAdmin}],
        items:[
          {title:"首頁",html:"首頁",id:"home"},
          {title:"差勤",html:"差勤"},
          {title:"管理",html:"管理",itemId:"admin"}
          ],
        renderTo:Ext.getBody()
        });
      function showHome(){
        tp.setActiveTab(Ext.getCmp("home"));
        }
      function showAdmin(){
        tp.setActiveTab(tp.getComponent("admin"));
        }

在範例 15-1 中, 我們用兩個按鈕分別將首頁與管理頁籤設成作用中, 但方式不同, 首頁頁籤裡指定 id 屬性, 而管理頁籤裡則指定 itemId 屬性. 在事件處理函式中存取這兩個頁籤時須分別呼叫 Ext.getCmp() 與 tp.getComponent() 方法.

OK, 回到刪除頁籤的問題, 上面範例 15 只能刪除新增的頁籤, 如果要刪除目前作用中的頁籤該怎麼做? 這就需要用到 getActiveTab() 方法了, 如範例 15-2 所示 :

測試範例 15-2 : http://mybidrobot.allalla.com/extjstest/tabpanel_15_2.htm [看原始碼]

      var tp=Ext.create("Ext.TabPanel",{
        title:"工作日誌系統",
        width:300,
        height:200,
        style:"margin:10px;",
        frame:true,
        defaults:{bodyPadding:10},
        buttons:[{text:"新增",handler:addTab},
                       {text:"刪除",handler:delTab}],
        buttonAlign:"left",
        items:[
          {title:"首頁",html:"首頁"},
          {title:"差勤",html:"差勤"},
          {title:"管理",html:"管理"}
          ],
        renderTo:Ext.getBody()
        });
      function addTab() {
        var idx=tp.items.length + 1;
        var newItem={title:"tab" + idx,html:"tab" + idx};
        var newTab=tp.add(newItem);
        tp.setActiveTab(newTab);
        }
      function delTab() {
        var tab=tp.getActiveTab();
        Ext.Msg.confirm("確認","確定要刪除頁籤 '" + tab.title + "'?",
          function(btn){
            if (btn=="yes"){var delTab=tp.remove(tab);}
            }
          );
        }

在上例中我們呼叫 getActiveTab() 取得作用中頁籤物件, 這樣就可以存取頁籤的屬性, 例如 title. 然後用一個確認框來詢問是否真的要刪除頁籤. 若按下 Yes 按鈕就呼叫 remove() 方法刪除之.

除了 add() 以外, 還有一個跟新增頁籤有關的方法 insert(index, tabComp), 可以指定頁籤物件要插入頁籤列成為索引 index, 如下範例 15-3 所示 :

測試範例 15-3 : http://mybidrobot.allalla.com/extjstest/tabpanel_15_3.htm [看原始碼]

      function addTab() {
        var idx=tp.items.length + 1;
        var newItem={title:"tab" + idx,html:"tab" + idx};
        var newTab=tp.insert(1,newItem);
        tp.setActiveTab(newTab);
        }

新增加的頁籤都會插入索引 1 的位置, 即首頁頁籤的後面.

下面範例 16 接著測試 items 頁籤項目的事件監聽屬性 listeners, 可以指定頁籤事件的監聽器.

測試範例 16 : http://mybidrobot.allalla.com/extjstest/tabpanel_16.htm [看原始碼]

      Ext.create("Ext.TabPanel",{
        title:"工作日誌系統",
        width:300,
        height:200,
        style:"margin:10px;",
        frame:true,
        defaults:{bodyPadding:10,closable:true},
        items:[
          {title:"首頁",html:"首頁",
           listeners:{
             beforeclose:function(){return false},
             beforedeactivate:function(){Ext.Msg.alert("資訊","離開首頁");}
             }
           },
          {title:"差勤",html:"差勤"},
          {title:"管理",html:"管理"}
          ],
        renderTo:Ext.getBody()
        });


在上例中, 我們在 defaults 屬性中設定 closable:true, 這樣每一個頁籤按鈕都會出現一個關閉小按鈕, 然後在首頁頁籤項目中, 添加一個 listeners 屬性, 設定監聽頁籤的 beforeclose 與 beforedeactivate 這兩個事件. 其中 beforeclose 屬性若為 fasle, 則頁籤關閉動作將不會執行, 所以即使首頁頁籤為 closable, 但按 X 鈕卻關不掉. 而 befroedeactivate 事件會在切換頁籤, 原作用中頁籤之狀態要改為非作用中時觸發, 上面我們是讓它去呼叫一個顯示訊息框的回呼函式, 每次從首頁要切換到別頁時就會顯示訊息框.

上面範例 10-2 我們測試過利用 tbar 工具列來切換頁籤圖示, 這裡我們改用頁籤的 listeners 來控制頁籤圖示之切換, 當從首頁切換到別頁時更換圖示; 切回首頁時恢復原來的圖示, 這要用到 beforedeactivate 與 beforeactivate 兩個事件, 如下範例 16-1 所示 :

測試範例 16-1http://mybidrobot.allalla.com/extjstest/tabpanel_16_1.htm [看原始碼]

      var tp=Ext.create("Ext.TabPanel",{
        title:"工作日誌系統",
        width:300,
        height:200,
        style:"margin:10px;",
        frame:true,
        defaults:{bodyPadding:10},
        items:[
          {title:"首頁",
           html:"首頁",
           tabConfig: {iconCls: 'icon-home'},
           listeners:{
             beforedeactivate:changeIconDeactive,
             beforeactivate:changeIconActive
             }
           },
          {title:"差勤",html:"差勤"},
          {title:"管理",html:"管理"}
          ],
        renderTo:Ext.getBody()
        });
      function changeIconDeactive() {
        tp.items.getAt(0).tab.setIconCls("icon-home-2");
        }
      function changeIconActive() {
        tp.items.getAt(0).tab.setIconCls("icon-home");
        }

下面範例 17 要測試頁籤物件的 setDiasabled() 方法. 首先用 buttons 屬性放置兩個按鈕, 左邊那個 "禁能" 鈕會讓作用中的頁籤變成不可用 (disabled), 而右邊那個 "致能" 鈕可以讓全部頁籤都變成可用 (enabled).

 測試範例 17 : http://mybidrobot.allalla.com/extjstest/tabpanel_17.htm [看原始碼]

    var tp=Ext.create("Ext.TabPanel",{
        title:"工作日誌系統",
        width:300,
        height:200,
        style:"margin:10px;",
        frame:true,
        defaults:{bodyPadding:10},
        buttons:[{text:"禁能",handler:setDisabled},
                     {text:"致能",handler:setAllEnabled}
                 ],
        items:[
          {title:"首頁",html:"首頁",itemId:"home"},
          {title:"差勤",html:"差勤"},
          {title:"管理",html:"管理"}
          ],
        renderTo:Ext.getBody()
        });
      function setDisabled(){
        var activetab=tp.getActiveTab();
        activetab.setDisabled(true);
        }
      function setAllEnabled(){
        for (var i=0; i<tp.items.length; i++) {
             tp.items.getAt(i).setDisabled(false);
             }
        }


上例中按下禁能鈕時, 先用 getActiveTab() 取得作用中頁籤物件, 再呼叫此頁籤之 setDisabled() 方法, 傳入 true 表示要 disable 此頁籤. 當按下致能鈕時, 先用頁籤面板的 items 屬性取得所有頁籤物件陣列, 其 length 屬性即為全部頁籤個數, 再用迴圈拜訪每一個頁籤物件, 呼叫 items 陣列之 getAt() 方法 (或 get 方法亦可) 即可取得指定索引位置之頁籤物件, 再呼叫 setDisabled() 方法, 傳入 false 表示要 enable 此頁籤, 這樣那些被禁能的頁籤就恢復為可用狀態了.

接著要測試的是頁籤物件的 hide() 與 show() 這兩個方法, 他們分別會將頁籤之按鈕或內容隱藏與顯示, 如下列範例 18 所示 :

測試範例 18http://mybidrobot.allalla.com/extjstest/tabpanel_18.htm [看原始碼]

      var tp=Ext.create("Ext.TabPanel",{
        title:"工作日誌系統",
        width:300,
        height:200,
        style:"margin:10px;",
        frame:true,
        defaults:{bodyPadding:10},
        buttons:[{text:"隱藏",handler:hideActive},
                     {text:"顯示",handler:showActive},
                     {text:"顯示全部",handler:showAll}],
        items:[
          {title:"首頁",html:"首頁"},
          {title:"差勤",html:"差勤"},
          {title:"管理",html:"管理"}
          ],
        renderTo:Ext.getBody()
        });
      function hideActive() {
        var activetab=tp.getActiveTab();
        activetab.tab.hide();
        activetab.hide();
        }
      function showActive() {
        var activetab=tp.getActiveTab();
        activetab.tab.show();
        activetab.show(); //無效, why?
        }
      function showAll() {
       for (var i=0; i
             tp.items.get(i).tab.show();
             tp.items.get(i).show();
             }
        }



上例中, 以 activeTab() 可以取得作用中的頁籤物件, 呼叫其 hide() 方法會隱藏頁籤內容, 但頁籤按鈕不受影響; 呼叫其 show() 方法照官網說明應該顯示頁籤內容, 但卻無效, why? 如果要控制頁籤按鈕的顯示與否, 要用頁籤物件的 tab 屬性先取得其按鈕物件, 再呼叫 show() 或 hide() 方法. 按下隱藏鈕時, 作用中頁籤的按鈕與內容部份會被隱藏, 按顯示則僅恢復顯示剛剛被隱藏的頁籤按鈕而已, 內容部分沒有恢復. 按顯示全部則所有被隱藏之頁籤都恢復顯示.

事實上 Ext.tab.Tab 類別還有一個 setVisible() 方法可以達成顯示與隱藏頁籤的功能, 我們把上例改為如下範例 18-1 :

測試範例 18-1 : http://mybidrobot.allalla.com/extjstest/tabpanel_18_1.htm [看原始碼]

      var tp=Ext.create("Ext.TabPanel",{
        title:"工作日誌系統",
        width:350,
        height:200,
        style:"margin:10px;",
        frame:true,
        defaults:{bodyPadding:10},
        buttons:[{text:"隱藏",handler:hideActive},
                      {text:"顯示",handler:showActive},
                      {text:"替換",handler:toggleState},
                      {text:"顯示全部",handler:showAll}],
        items:[
          {title:"首頁",html:"首頁"},
          {title:"差勤",html:"差勤"},
          {title:"管理",html:"管理"}
          ],
        renderTo:Ext.getBody()
        }); 
      function hideActive() {
        var activetab=tp.getActiveTab();
        activetab.tab.setVisible(false);
        activetab.setVisible(false);
        }
      function showActive() {
        var activetab=tp.getActiveTab();
        activetab.tab.setVisible(true);
        activetab.setVisible(true); //無效, why?
        }
      function toggleState() {
        for (var i=0; i
             var tab=tp.items.get(i);
             var tabbtn=tab.tab; //取得頁籤按鈕
             tabbtn.setVisible(!tabbtn.isVisible());
             tab.setVisible(!tab.isVisible());              }
        }
      function showAll() {
       for (var i=0; i
             var tab=tp.items.get(i);
             var tabbtn=tab.tab;
             tabbtn.setVisible(true);
             tab.setVisible(true);
             }
        }

在上例中, 我們把 show/hide 改成用 setVisible 去做, 同時加了一個 "替換" 按鈕, 按下去就顯示已經隱藏的頁籤, 把目前顯示的隱藏起來. 但是同樣的, 頁籤內容的 setVisible(true) 跟 show() 一樣都無效, 不知何故也.

以上所有範例中, 頁籤內容都是在 items 的 html 屬性中設定, 若要在程式中動態加以改變該怎麼做呢? 這就要用到 update() 方法了, 如下列範例 19 所示 :

測試範例 19 : http://mybidrobot.allalla.com/extjstest/tabpanel_19.htm [看原始碼]

      var tp=Ext.create("Ext.TabPanel",{
        title:"工作日誌系統",
        width:300,
        height:200,
        style:"margin:10px;",
        frame:true,
        defaults:{bodyPadding:10},
        buttons:[{text:"更新",handler:changeContent}],
        items:[
          {title:"首頁",html:"首頁"},
          {title:"差勤",html:"差勤"},
          {title:"管理",html:"管理"}
          ],
        renderTo:Ext.getBody()
        });
      function changeContent() {
        var activetab=tp.getActiveTab();
        activetab.update("<a href='http://tw.yahoo.com' target='_blank'>Yahoo</a>");
        }



上例中, 按下更新按鈕, 事件處理函式先用 getActiveTab() 取得作用中頁籤物件, 再呼叫其 update() 方法更新頁籤內容, 內容可以是純文字/HTML 字串, 或者是 ExtJS 元件. 事實上 update() 方法 還有兩個備選參數如下 :

update(htmlOrData,[loadScripts],[callback])

其中 loadScripts 為布林值 (預設 false), 只有當第一參數為 HTML 時才有效, 而 callback 為更新後之回呼函式. 下面範例 19-1 即測試其功能 :

測試範例 19-1 : http://mybidrobot.allalla.com/extjstest/tabpanel_19_1.htm [看原始碼]

      function changeContent() {
        var activetab=tp.getActiveTab();
        var html="<a href='http://tw.yahoo.com' target='_blank'>Yahoo</a>" +
                      "<script>alert('更新完成!')<\/script>";
        activetab.update(html,true,callback);
        }
      function callback() {
        Ext.Msg.alert("訊息","更新完成!");
        }

在上例中, 我們將 loadScript 設為 true, 並指定回呼函式, 因此更新頁籤內容時, 其中的 script 會被載入, 並被立即執行, 因 alert() 有強制性, 須等按下 alert() 之確定後才會執行回呼程式. 如果把 localScript 設為 false, 則不會載入 script, 因此會直接執行回呼程式.

上面兩個範例是在程式裡面更新頁籤內容, 如果要從後端伺服器載入內容該怎麼做? 這要在頁籤物件中設定 autoLoad 屬性, 如下面範例 20 所示. 注意, 以下範例因為要從同網域中載入檔案, 所以都只能在伺服器上執行, 不能在本地執行.:

測試範例 20 : http://mybidrobot.allalla.com/extjstest/tabpanel_20.htm [看原始碼]

      var tp=Ext.create("Ext.TabPanel",{
        title:"工作日誌系統",
        width:300,
        height:200,
        style:"margin:10px;",
        frame:true,
        defaults:{bodyPadding:10},
        items:[
          {title:"首頁",html:"首頁"},
          {title:"差勤",html:"差勤",
          autoLoad:{url: "tabpanel_autoload_1.php"}
          },
          {title:"管理",html:"管理"}
          ],
        renderTo:Ext.getBody()
        });

在此例中, 我們在第二個頁籤加入 autoLoad 物件, 並設定其 url 屬性為同網域中的 php 檔案, 其內容如下 :

<?php
date_default_timezone_set("Asia/Taipei");  
echo date("Y-m-d H:i:s")." 載入<br>";
?>

亦即它會回應目前的台北時間, 如下圖所示 :


當切到差勤頁籤時,  就會從伺服器載入該網頁, 並覆蓋掉 html 屬性中原本的 "差勤" 兩字. 這 autoLoad 屬性為一個物件, 亦即還可以放其他屬性, 例如可以用 params 屬性傳遞參數給後端伺服器, 並且設定 scripts 為 true, 如下列範例 20-1 所示 :

測試範例 20-1 : http://mybidrobot.allalla.com/extjstest/tabpanel_20_1.htm [看原始碼]

      var tp=Ext.create("Ext.TabPanel",{
        title:"工作日誌系統",
        width:300,
        height:200,
        style:"margin:10px;",
        frame:true,
        defaults:{bodyPadding:10},
        items:[
          {title:"首頁",html:"首頁"},
          {title:"差勤",html:"差勤",
            autoLoad:{
              url:'tabpanel_autoload_2.php',
               scripts:true,
               params:{param1:"Hello ",param2:"World!"}
              }
           },
          {title:"管理",html:"管理"}
          ],
        renderTo:Ext.getBody()
        });

而要載入的後端網頁 tabpanel_autoload_2.php 如下 :

<?php
date_default_timezone_set("Asia/Taipei");  
echo date("Y-m-d H:i:s")." 載入<br>";
echo $_REQUEST["param1"].$_REQUEST["param2"];
?>
<script>
  alert("autoLoad 載入完成!");
</script>

結果如下圖所示, 所傳遞之參數有送回來, 但是 scripts 卻沒作用, why?


當然, 如果把參數附在 url 字串後面 (?param1=Hello&param1=World), 使用 GET 方式而不用 params 屬性也是可以的.

載入檔案除了 autoLoad 屬性外, 也可以使用 loader 屬性, 如下列範例 21 所示.

測試範例 21 : http://mybidrobot.allalla.com/extjstest/tabpanel_21.htm [看原始碼]

      var tp=Ext.create("Ext.TabPanel",{
        title:"工作日誌系統",
        width:300,
        height:200,
        style:"margin:10px;",
        frame:true,
        defaults:{bodyPadding:10},
        items:[
          {title:"首頁",html:"首頁"},
          {title:"差勤",html:"差勤",
           loader:{
             url:"tabpanel_autoload_1.php",
             autoLoad:true,
             scripts:true
             }
           },
          {title:"管理",html:"管理"}
          ],
        renderTo:Ext.getBody()
        });


注意, 這裡 loader 物件的 autoLoad 屬性一定要設為 true (不設的話預設為 false), 否則不會載入指定之檔案. 此例中的 Script 也是沒有執行.

以上範例不管是使用 loader 還是 autoLoad, 都只會載入檔案一次, 因為切到別頁再切回來時, 仍然顯示同樣的時間, 可見沒有再次載入. 如果要在每次切換頁籤時都重新載入檔案該怎麼做? 這必須用 listeners 去監聽 activate 事件, 當頁籤變成作用中狀態時, 就讓 tab 物件重新載入一次, 如下列範例 22 所示 :

測試範例 22 : http://mybidrobot.allalla.com/extjstest/tabpanel_22.htm [看原始碼]

      var tp=Ext.create("Ext.TabPanel",{
        title:"工作日誌系統",
        width:300,
        height:200,
        style:"margin:10px;",
        frame:true,
        defaults:{bodyPadding:10},
        items:[
          {title:"首頁",html:"首頁"},
          {title:"差勤",html:"差勤",
           loader: {
             url:"tabpanel_autoload_2.php",
             autoLoad:true,
             loadMask:true,
             scripts:true
             },
           listeners:{activate:function(tab){tab.loader.load();}}
           },
          {title:"管理",html:"管理"}
          ],
        renderTo:Ext.getBody()
        });

由此例可發現, 如果在 listeners 中觸發載入檔案, 這樣 script 就可以執行了. 此處我們監聽 activate 事件, 因此每次切換到 "差勤" 頁籤時, 就會重新自伺服器載入 (可觀察時間已有改變), script 也會再次執行.

在上例中, 我們在 loader 物件裡添加了 loadMask:true 屬性, 這樣在載入檔案時, 會顯示 "Loading ... " 的對話框, 直到載入結束, 若未設此屬性 (預設 false), 就不會顯示.

如果我們不想每次都從伺服器載入, 只想於第一次載入後就在本機中執行, 這樣就不能監聽 activate 事件, 要改為 render 事件, 如下範例 22-1 所示 :

測試範例 22-1 : http://mybidrobot.allalla.com/extjstest/tabpanel_22_1.htm [看原始碼]

listeners:{render:function(tab){tab.loader.load();}}

但這樣一來 script 就只有在初次從伺服器載入時才會執行一次, 之後切換到差勤頁籤就不會執行 script 了.

在網路上看到這篇 :

http://www.cnblogs.com/letao/archive/2012/05/18/2507234.html

想說照其作法測試看看, 我把上面範例 15 拿來改成下面範例 22-2  :

測試範例 22-2 : http://mybidrobot.allalla.com/extjstest/tabpanel_22_2.htm [看原始碼]

       var tp=Ext.create("Ext.TabPanel",{
        title:"工作日誌系統",
        width:300,
        height:200,
        style:"margin:10px;",
        frame:true,
        defaults:{bodyPadding:10},
        buttons:[{text:"新增",handler:addTab},
                     {text:"刪除",handler:delTab}],
        items:[
          {title:"首頁",html:"首頁"},
          {title:"差勤",html:"差勤"},
          {title:"管理",html:"管理"}
          ],
        renderTo:Ext.getBody()
        });
      function addTab() {
        var idx=tp.items.length + 1;
        var newItem={title:"tab" + idx,
                     itemId:"tab" + idx,
                     loader:{url:"tabpanel_autoload_3.php",
                             autoLoad:true,
                             params:{param1:"Hello ",param2:"World!"},
                             autoMask:true,
                             scripts:true
                             },
                     listeners:{render:function(tab){tab.loader.load();}}
                     };
        var newTab=tp.add(newItem);
        tp.setActiveTab(newTab);
        }
      function delTab() {
        var idx=tp.items.length;
        var delTab=tp.remove("tab" + idx, true);
        }
      });

而載入的 php 檔案如下, 這回我們將 alert 放在函式中, 利用按鈕來觸發, 而不是放在頂層 (只在載入時執行一次) :

<?php
header('Content-type: text/html; charset=utf-8');
date_default_timezone_set("Asia/Taipei");  
echo date("Y-m-d H:i:s")." 載入<br>";
$params=$_REQUEST["param1"].$_REQUEST["param2"];
echo '<button onclick="javascript:show()">確定</button>'.
        '<script>'.
        'function show() {'.
        '  alert("'.$params.'");'.
        '  }'.
        '</script>';
?>


按下新增鈕就會增加一個頁籤, 並從伺服器載入含有 script 的內容, 因為 scripts 設為 true, 因此按確定鈕會呼叫函式 show(), 顯示 "Hello World!". 由於監聽的是 render 事件, 僅新增頁籤時才載入檔案, 因此切換頁籤時間不會變化.

OK, 以上便是近三周來對 TabPanel 的測試, ExtJS 真難搞啊.