2009年5月14日 星期四

jQuery UI 對話視窗


javascript 內建的 alert 視窗按鈕選項,只有【確定】、【取消】兩個。但偏偏我的客人執意要【是】、【否】、【取消】。以前我可能會告訴她做不到,但時下景氣這麼差,只好找些替代方案來取悅人家了。

在 jQuery 1.3 版後,同時 jQuery UI 也發佈了 1.7.1 版本。在這新的 jQuery UI 裡,widgets >> Dialog 所內建的對話盒真的漂亮許多,且她提供的 Modal Dialog  模式剛好可以取代原先 alert 的效果,也可以自訂 Buttons。


可自行到 Download 頁籤下載,壓縮檔解開後,/css 與 /js 資料夾是程式開發所必要的。可以將這兩個資料夾複製到自己開發的專案下面。接著要介紹的,就是自己撰寫一個使用 jQuery UI Dialog 的程式。


< script type="text/javascript" > 
$.ui.dialog.defaults.bgiframe = true;
$(function() {
$("#dialog").hide();
$("#dialog").dialog({   buttons:{
"取消":function(){  $("input[id*=h_DeliverType]").val('0'); $(this).dialog("close");},
"否":function(){  $("input[id*=h_DeliverType]").val('2'); $(this).dialog("close"); },
"是":function(){  $("input[id*=h_DeliverType]").val('1'); $(this).dialog("close"); }       
},
modal: true,
autoOpen: false,
close: function(event,ui){alert('使用者按 X 離開視窗');}

});  
});

function ShowDeliverDialog()
{
$('#dialog').dialog('open');
}

< /script >

< /head >
< body >
< div class="demo" > 

< div id="dialog" title="對話視窗"> 
如果您要吃披薩,請按 '是' ,如果不要吃披薩,請按 '否'。如果目前不想選擇,請按 '取消'。
< /div >




在測試頁面,先置放一個 id=dialog 的 div,當作我們對話視窗的說明文字,title 屬性則是對話視窗最上層會顯示的文字。我希望這個對話視窗內容,是要在 User 壓了 button 按鈕後才出現,所以預設會用  jQuery 的 .hide() 方法將他隱藏。jQuery UI 的 Dialog 有一個 Options 選項叫做 buttons ,你可以在這裡定義自己所要新增的 button 以及這個 button 所要觸發的事件。modal 屬性設為 true ,則表示這個對話視窗是以獨占方式顯示,其背景物件都會以暗化來處理。autoOpen 屬性設為 false ,則是因為我們不希望對話視窗在頁面一載入就跳出,而是要等按鈕觸發後才跳出。

完成設定後,就會跟上圖結果一樣了。

補充: 20091202
在程式裡面加上 攔 close 的事件,也就是當 user 按了視窗右上角的 X 符號離開視窗時會觸發的事件。

$('.selector').dialog({
   close: function(event, ui) { ... }
});

參考網址:http://jqueryui.com/demos/dialog/#event-close  「請選擇 Events 頁籤」

2009年4月4日 星期六

努力休息


就身體結構而言,休息的期間才是真正在修補、重建的時機。而我們在運動、勞作期間,對身體來說,都是在破壞、消耗。

很少人真正了解休息所帶給我們生理上的益處,尤其是睡眠。在睡眠當中,身體會釋出生長激素來修補日間訓練所造成的損傷,並且支撐住因訓練而衰弱的任何系統(註一)。這個觀點我很肯定,因為小時常常熬夜看書,沒有分泌到生長激素,唉~ 。不知道現在還會不會分泌?


眼下有許多人,包括我自己,都有著數字迷思,今天 5 公里,明天 6 公里,後天 7 公里 ....。多跑多健康,有騎有保庇。可是我們都不知道,如此強大的破壞力道,身體的復原是否跟的上腳步。如果跟的上,你就會得到阿諾的『胸肌』;如果跟不上,你就會有一雙『天殘腳』。



所以別忘囉!要努力休息~






※註一:出自『鐵人三項訓練聖經』




延伸閱讀:

2009年4月2日 星期四

與 jQuery 的第一次相遇




剛開始接觸人氣指數很高的 jQuery 時,難免會跟這陌生的人氣王有著大家都會想問的問題。就如同去參加聯誼時,大家老是脫離不了問對方姓名、興趣、假日都在做些什麼...?


第一個問題:為何要將程式放在 $(document).ready( ) 裡?
當網頁被開啟時,頁面上可能同時放置了許多同片、Banner...等等,這時如果使用 javascript去執行,可能會發生頁面上的物件都還沒完全下載就被存取的問題發生。所以,以往在 javascript 都會用以下方式避免:

window.onload = function(){ alert("welcome"); }

在jQuery,則是使用:


$(document).ready( function()
{
// ... your code
});


可參考:http://docs.jquery.com/How_jQuery_Works

第二個問題:以 $( ) 包起來的標記代表什麼意思?
在jQuery,以 $( ) 標記表示所謂的選取元素( Selectors) 。舉例來說,在 JavaScript 要抓取一個 ID 為 tbx_Account 的物件,會用 document.getElementsById("tbx_Account"),但在 jQuery 只需要使用 $("#tbx_Account") 就可以了。

列舉常用的例子:
$("a"); // 取得頁面中所有的<a>標籤元素
$("#tbx_account"); // 取得id為tbx_account的元素
$(".UsrDefine"); // 取得class name為UsrDefine的所有元素
$('#container a'); // 取得id為container之元素其內部的所有連結
$("div > p"); // 取得div父元素其下所有的p子元素
$("tr:first"); // 取得第一個找到的tr標籤元素
$("tr:even"); //取得所有 tr 標籤元素的偶數集合
$("tr:odd"); //取得所有 tr 標籤元素的奇數集合
$("input[name='account']"); // 取得其name屬性值為account的input元素
[注意] 使用 指定屬性方式時,記得中括號與前方的指定類別不可留空白


[補充] :具有AND 與 OR 的效果。
AND : $("  table tr td span "); // 這就會去找 在 table下  且  在 tr 下   且在  td 下  的 span
OR: $("div , span"); // 這就會去找  div  或  span
[補充]:如果您取得的結果是個多項目的集合,若要取得這集合裡的項目數,可以使用 .size() 來取得。
範例:alert($("input[name=pet]:checked").size());

[補充]:如果畫面上有好多 TextBox,你想要統計 TextBox 目前有值的數目,可以使用下面的語法:
alert($("input[id$=tbx_psd_carstyleno1][value!='']").length );

這裡連續使用了兩次指定屬性的方式,表示這屬性的條件可以讓我們用 And 的方式一直增加。而 value 其實也是 input 的屬性的一部分,而  !=''  是用來找出不是空白的項目。


第三個問題:什麼是 chaining (串接)?
參閱jQuery的屬性[http://docs.jquery.com/Attributes],發現大部分屬性的回傳值都是 jQuery物件。舉例來說:

$("#container").css("color", "blue") --> 回傳 jQuery

$("#container").css("background-color", "red") --> 回傳 jQuery

當設定完第一個屬性後,回傳了一個 jQuery 物件,如果我們把這個回傳物件繼續加以使用,就不須要像第二行指令這樣從頭再指定 $("#container") 了,而是接在第一行屬性設定的後面。這種作法可以大大減少程式設計師重複撰寫相同的程式。

第四個問題:怎麼加入事件( event ) ?
網頁上的控制項,免不了要去撰寫 blur( ) 、 click( )、change( ) ... 相關事件。查閱 jQuery 的 Event , click ( )  與 click ( fn ) 都是成對出現的,同樣的情況也出現在 blur( ) , change( ) ...。這其中的差異,就是 click ( ) 是去觸發 「點選」動作的事件,click ( fn) 則是去撰寫 「點選」動作後要做的事情。

舉例來說:今天需要判斷一個ID為 tbx_Account 的文字輸入對話盒物件是否為空,則可以用以下語法來完成:


$(document).ready(function() {
$("#tbx_Account").blur(function()
{
if(!this.value)
{
alert("Please enter some text!");
}
else
{
alert(this.value);
}
})
});


在tbx_Account,去實作 blur 事件觸發時要執行的動作。同時使用了 this.value 去抓目前這個物件的值,來判斷 user 是否有輸入資料。

第五個問題:如何使用 Ajax ?
提到 Ajax 時,以前針對不同的瀏覽器,宣告 XMLHTTP 的方式會不同。但 jQuery 把這些繁瑣的工作自動幫我們處理好了。只需要使用 jQuery 提供的方法即可完成。

以一個簡單例子來說明:


<html>
<body>
<form id="form1" runat="server">
<div>
Get Now Date:<span id="spTime"></span>
</div>
</form>
</body>
</html>


我們希望 <span>: spTime 標籤會去呼叫一支自動回傳現在時間的 AjaxGetTime.aspx 。則在jQuery 可以用下面寫法:


<script>
$(document).ready( function(){
$("#spTime").load("AjaxGetTime.aspx");
});
</script>


的確,只需一行 .load( ) 就可以完成 Ajax 的使用。
完整的 .load( url, data, callback)  語法裡,
url: 是指所叫用的遠端程式網址,是必填的參數。
data: 則是當你在呼叫遠端程式時,有需要進一步傳其它參數時使用。它可以是 key / value 成對出現或是字串( jQuery Ver:1.3  開始支援)。
callback: 則是當遠端程式完成後,接著要執行的程式。這裡的 callback 名稱取的好,「呼叫(遠端程式)回來後(要做的事情)」。不管成功或失敗都會執行。


擷取 jQuery 官網上的一段 code 來參考 .load(url, data, callback ):


$("#feeds").load("feeds.php", {limit: 25}, function(){
alert("The last 25 entries in the feed have been loaded");
});



如果簡單的 .load( ) 不能滿足您的需求,可以進一步去參考 .get( ) 、.post( ) 語法。
延伸閱讀:http://webdesign.enjoyitsimply.com/jquery/jquery_ajax [中文]

第六個問題:為什麼一定非 $ 不可?
在使用 jQuery 時,常常會以 $ 符號當作開始,而這 $ 符號,其實是 jQuery 的縮寫。如果你不想使用縮寫,直接打 jQuery 也是可以的。

舉例來說:

$("#tbx01").val('aa');
jQuery("#tbx01").val('aa');

這兩行的指令是功能是一樣的。

另外,如果你的程式除了 jQuery 之外,還有使用其他如:Prototype, MooTools, YUI 程式,可能別的程式已經用 $ 當作他們程式的縮寫,這情況就很容易造成混淆。所以,你可以使用 jQuery.noConflict();  來宣告以下程式要使用 jQuery ,而不是其他的。

<script>
 jQuery.noConflict();
 
 // Use jQuery via jQuery(...)
 jQuery(document).ready(function(){
   jQuery("div").hide();
 });
 
 // Use Prototype with $(...), etc.
 $('someid').hide();
</script>

或許能夠用比較簡短的縮寫,是比較吸引人的。於是可以用以下方法,讓 $j 來代替原先的 $。

var $j = jQuery.noConflict();

所以程式就可以寫成:

<script>
 var $j = jQuery.noConflict();
 
 // Use jQuery via $j(...)
 $j(document).ready(function(){
   $j("div").hide();
 });
 
 // Use Prototype with $(...), etc.
 $('someid').hide();
</script>

參考來源:
1.http://blog.wu-boy.com/2008/09/22/412/
2.http://docs.jquery.com/Using_jQuery_with_Other_Libraries
3.http://jsgears.com/thread-63-1-1.html

2009年3月24日 星期二

Page and Extent In SQL Server

在SQL Server 裡,最基本的儲存單位為 page。當一個新 Table 被建立時,就會在硬碟裡建立一塊以 8K 的連續空間,以便儲存我們想要保留的資料。這 8K 的連續空間,正是 SQL Server 的基本儲存單位: Page。當這 8K 的空間用完時,又會自動再產生一個 8K 的新 page。

如右圖所示,每個 page 最前面會先放一個 96 byte 的 Page header,在 Page header 裡頭紀錄了 page number, page type, page 剩餘空間, 以及配置單位編號。

page number 的分配是由 0 - 7,因為每 8 個 page 在 SQL Server 裡就會被視為一個 Extent。

page type 則是定義了以下格式:Data, Index, Text/Image, Global Allocation Map, Shared Global 
Allocation Map,Page Free Space, Index Allocation Map, Bulk Changed Map, Differential Changed Map。

Data row 則是緊接在 Page header 的後面。而放在最後面的則 Row offsets。Row offsets 它擺放的順序是從頁尾開始,主要是記載著每個 Data row 它離頁首的距離。

雖然 page 是最基本的儲存單位,但 SQL Server 為了在空間管理上更有效率,它將 8 個連續的 page 空間 ( 64 K) 視為一個 Extent。但如果你的資料量很小,則 SQL Server 就不會主動幫你建立一個 Extent。

Extent 因裡面 8 個 page 的型態,而有 Uniform extents 與 Mixed extents。簡言之,如果你的 8 個 page 都是同一個型態,則稱為  Uniform extents ,反之則為  Mixed extents。

以上圖為例, Mixed extent 裡頭 同時包含了 table2, table3, index1, index2, index3 等等的混何型態,所以稱為 Mixed extent。而 Uniform extent 裡面的 8 個 page 則都是 table1。





文獻參考:

2009年3月20日 星期五

動態置換 Master Page

在 .Net 2.0 提供了 Master Page 功能,可以讓我們將事先定義好的樣板快速的套用在各個開發頁面。接下來要介紹的,是讓一個頁面可以透過程式動態的去變更頁面的 Master Page。

舉個例子來說,我們設計了兩個 Master Page,一個叫做 MTeacher.master (專門提供給老師看的樣板),另一個叫 MStudent.master(專門提供給學生看的樣板)。當檢視我們頁面的使用者是老師時,就要選用 MTeacher.master,當視我們頁面的使用者是學生時,就要選用 MStudent.master。

直覺的,很像可以在程式裡直接去設定  MasterPageFile ,如下所示:


protected void Page_Load(object sender, EventArgs e)
{
MasterPageFile = "MTeacher.master";
}

但回傳的結果卻是:
'MasterPageFile' 屬性只能在 'Page_PreInit' 事件內或之前設定。

根據錯誤訊息的提示,如果要設定 MasterPageFile ,必須要在 Page_PreInit 事件執行才可以。所以,改採用 覆寫 Page_PreInit 事件,應該就可以了。動手改一下程式如下:

protected override void OnPreInit(EventArgs e)
{
MasterPageFile = "MStudent.master";
string strType = "" + Request["isTeacher"];

if(strType=="1")
MasterPageFile = "MTeacher.master";


base.OnPreInit(e);
}


為了簡單起見,透過 URL 參數 isTeacher 來決定登入者角色是否為老師。 當 isTeacher 是 1 時,即表示目前是老師在檢視頁面;如果不是 1 ,則為學生。程式修改後,就可以正確的呈現出我們所希望的樣子了。

當你有需要更換 MasterPage 樣板時,記得要覆寫 Page_PreInit 事件,並在這事件裡完成你的程式。

2009年3月16日 星期一

為什麼我的 Button 沒有呼叫 __doPostBack

一如往常所見,檢視網頁原始碼時,可以找到 __doPostBack 的 JavaScript 宣告。同時包含著兩個 hidden 欄位的宣告: __EVENTTARGET, __EVENTARGUMENT。


<input type="hidden" name="__EVENTTARGET" id="__EVENTTARGET" value="" />
<input type="hidden" name="__EVENTARGUMENT" id="__EVENTARGUMENT" value="" />

function __doPostBack(eventTarget, eventArgument) {
if (!theForm.onsubmit || (theForm.onsubmit() != false)) {
theForm.__EVENTTARGET.value = eventTarget;
theForm.__EVENTARGUMENT.value = eventArgument;
theForm.submit();
}
}

__EVENTTARGET 用來儲存是誰觸發了 PostBack。
__EVENTARGUMENT 則是用來儲存這次的 PostBack 是否有其它參數須一併傳遞。

以 DropDownList 為例,在網頁放置一個 DropDownList 後,它的原始碼變為

<select name="DropDownList1" onchange="javascript:setTimeout('__doPostBack(\'DropDownList1\',\'\')', 0)" id="DropDownList1">
<option value="A">A</option>
<option value="B">B</option>
</select>

當你的 DropDownList 的挑選項目被更動之後,就會觸發__doPostBack( )。eventTarget指的是造成發生PostBack的控制項是誰?而 eventArgument則是觸發PostBack同時,是否有其他參數需要傳遞。
在範例中,當發生PostBack時,就可以知道造成PostBack的就是 DropDownList1 ,而此次並沒有其他參數傳遞,所以是空字串。__doPostBack( ) 此時會將2個隱藏欄位(__EVENTTARGET,__EVENTARGUMENT)作一次更新後再將本頁面送出(Submit)。透過這種機制,送出後新載入的頁面,就可以透過
string strTarget = ""+Request.Form["__EVENTTARGET"];
來取得觸發PostBack的物件是誰了。
 

然而,這種機制並不適合 Button 與 ImageButton 兩個控制項。
現在再拉一個Button控制項並檢視它的原始碼。

<input type="submit" name="Button1" value="WebControl" id="Button1" />

可以看到Button的原始碼並沒有去呼叫__doPostBack( )。而先前提到的兩個隱藏欄位 __EVENTTARGET,__EVENTARGUMENT 也就不會被更新,當頁面送出後,還可以透過 Request.Form["__EVENTTARGET"] 去抓到觸發 PostBack 的控制項是誰嗎?試著測試一下,您會發現得到的是空白。所以,為何 Bubbon 或 ImageButton 在 PostBack 之後的頁面無法透過 Request.Form["__EVENTTARGET"] 抓到值,主要是它產生的程式碼裡並非透過 __doPostBack( )來實作,也沒有留下紀錄到 __EVENTTARGET 隱藏欄位裡頭,所以才會抓不到資料。
 

雖然 Button 或 ImageButton 沒有辦法讓送出後的頁面抓到 __EVENTTARGET 的值,但可以有其他的選擇。捨棄 Sever Control 改用 Html Control。在 VS(Visual Studio) 的工具箱裡,選擇 HTML 的群組,拉一個 HTML Button 控制項出來,同時將這個控制項的屬性設定為【以伺服器控制項執行】(其實就是設定 runat="server"),接著在 HTML Button 快點兩下,以產生 onserverclick 事件。做完這些動作後,檢視程式碼可以發現如下的改變:

<input language="javascript" onclick="__doPostBack('IamHtmlButton','')" name="IamHtmlButton" type="button" id="IamHtmlButton" value="HtmlControlinServer" />

這個HTML Button 使用了 __doPostBack( ) ,不意外的,我們可以在送出後的頁面抓到 __EVENTTARGET 裡面所記錄的值,就是這個 HTML Button。
 

參考資料:
[1]Understanding the JavaScript __doPostBack Function
[2]藍色小舖

2009年1月9日 星期五

Server端註冊client端的Javascript

在ASP.net ,如果從 server 端想在 client 端註冊 Javascript,可以使用

RegisterStartupScript(key, script)


RegisterClientScriptBlock(key, script)




雖然都可以達到註冊 Javascript 的目的但他們實際上是有所差異的。




RegisterClientScriptBlock( ),他會將我們自訂的 JavaScript 產生在
<form runat="server"> 標籤之後,你可以在檢視網頁原始碼

時看到他。





RegisterStartupScript( ),則是產生在 </form> 標籤之前。

產生的先後順序,會造成影響的地方,在於觸發 Client 端 JavaScript
的時機。 假設你有一個 JavaScript 要將滑鼠游標停駐在某一個 TextBox,
如果你將 JavaScript 放在 <form runat="server">之後,則 TextBox 
的 HTML Code 尚未來得及產生,卻硬是要去設定游標位置,這時就會出現
JavaScript的錯誤。在這情況裡,選擇 RegisterStartupScript 會是個合適的
選擇。

參考網址:http://msdn.microsoft.com/en-us/library/aa478975.aspx
(Injecting Client-Side Script from an ASP.NET Server Control)