Unobtrusive Javascript是一種將Javascript從HTML結構抽離的設計概念(延伸閱讀: kewang, ericsk),避免在HTML標籤中夾雜onchange、onclick Attribute掛載Javascript事件,讓HTML歸HTML、Javascript歸Javascript,功能權責清楚區分,HTML也變得清爽容易閱讀。

ASP.NET MVC 3加入的Unobtrusive jQuery Validation,徹底實踐了Unobtrusive Javascript的精神,透過在<input>加上data-val-*等HTML5相容Attribute註記,不需額外撰寫Javascript程式就能為欄位加上客戶端驗證邏輯。

手邊的專案一時半刻還無法都轉成ASP.NET MVC3,但又想學習這種Client Validation的優雅寫法,該怎麼辦? 搞懂jquery.validate.unobtrusive.js的原理,只要在HTML中加入適當的Attribute,我們也能實現不沾手的客戶端驗證寫法。

Brad Wilson有篇詳細的Unobtrusive Client Validation in ASP.NET MVC 3介紹,參考該文章及jquery.validate.unobtrusive.js原始碼,我將手動引用的重點整理如下:

  1. 參照jquery.js、jquery.validate.js、jquery.validate.unobtrusive.js (可透過Microsoft Ajax CDN提供)。
  2. <input>元素加上data-val=”true”,註明要啟用客戶端驗證。data-val-rulename=”驗證失敗訊息"則為<input>加上各式的規則,例如: data-val-required="姓名欄位不可空白"。
  3. 有些驗證規則需要額外參數,則可透過data-val-rulename-parameterName方式指定,例如: data-val-length-max=”60”。
  4. 在<input>後方加上<span data-valmsg-for=”inputName” data-valmsg-replace=”true/false”></span>標註顯示錯誤訊息的位置 。
  5. 如果錯誤訊息要採彙總顯示,則放入一個<div data-valmsg-summary="true"></div>

以下範例一口氣把Unobtrusive jQuery Validation內建支援的檢核規則(未包含creditcard及remote)整理在同一個網頁中,可做為應用時的參考。另外,這套檢核機制底層採用的是頗受歡迎的jQuery Validation Plugin,可視需求輕鬆加入自訂的檢核邏輯,擴充性很好,不用怕被奇怪的專案需求考倒,大家可安心服用。

<!DOCTYPE html>
<html>
<head>
    <title>jQuery Validate Unobtrusive</title>
    <script src="http://ajax.aspnetcdn.com/ajax/jQuery/jquery-1.6.2.js" 
     type="text/javascript"></script>   
    <script src="http://ajax.aspnetcdn.com/ajax/jquery.validate/1.8.1/jquery.validate.js" 
     type="text/javascript"></script>   
    <script src="http://ajax.aspnetcdn.com/ajax/mvc/3.0/jquery.validate.unobtrusive.js" 
     type="text/javascript"></script>   
    <style type="text/css">
body,input { font-size: 9pt; }     
.input-validation-error { border: 1px solid #ff0000 }
.input-validation-valid  { border: 1px solid #00ff00 }
.field-validation-error { color: #ff0000 }
.field-validation-valid { display: none }
.validation-summary-errors { font-weight: bold; color: #ff0000 }
.validation-summary-valid { display: none }
    </style>
</head>
<body>
<form id="form1" method="get">
    <div data-valmsg-summary="true"><ul></ul></div>
    <div>
    <input type="text" id="tReq" name="tReq" data-val="true" 
     data-val-required="不可空白"/>
    <span data-valmsg-for="tReq"></span>
    </div>
    <div>
    <input type="text" id="tAccept" name="tAccept" value="a.doc" 
     data-val="true" data-val-accept="檔名須為.jpg、.gif或.png"
     data-val-accept-exts="jpg|gif|png"
     />
    <span data-valmsg-for="tAccept"></span>
    </div>
    <div>
    <input type="text" id="tRegex" name="tRegex" value="123-ABC@"
     data-val="true" data-val-regex="車牌格式須為999-999"
     data-val-regex-pattern="[0-9A-Z]{3}-[0-9A-Z]{3}"
     />
    <span data-valmsg-for="tRegex"></span>
    </div>
    <div>
    <input type="text" id="tDigit" name="tDigit" value="-1234"
     data-val="true" data-val-digits="只接受數字字元"/>
    <span data-valmsg-for="tDigit"></span>
    </div>
    <div>
    <input type="text" id="tNum" name="tNum" value="-1,234.56A"
     data-val="true" data-val-number="必須為有效數字"/>
    <span data-valmsg-for="tNum"></span>
    </div>
    <div>
    <input type="text" id="tDate" name="tDate" value="X/01/X2000"
     data-val="true" data-val-date="必須為日期(僅粗略檢查)"/>
    <span data-valmsg-for="tDate"></span>
    </div>
    <div>
    <input type="text" id="tEmail" name="tEmail" value="jeffrey @mail.com"
     data-val="true" data-val-email="必須為Email"/>
    <span data-valmsg-for="tEmail"></span>
    </div>
    <div>
    <input type="text" id="tUrl" name="tUrl" value="http:// blog.darkthread.net"
     data-val="true" data-val-url="必須為有效網址"/>
    <span data-valmsg-for="tUrl"></span>
    </div>
    <div>
    <input type="text" id="tLen" name="tLen" value="TTT"
     data-val="true" data-val-length="長度須介於4到8之間"
     data-val-length-min="4" data-val-length-max="8"
     />
    <span data-valmsg-for="tLen"></span>
    </div>
    <div>
    <input type="text" id="tRange" name="tRange" value="5"
     data-val="true" data-val-range="須介於10到100" 
     data-val-range-min="10" data-val-range-max="100"
     />
    <span data-valmsg-for="tRange"></span>
    </div>
    <div>
    <input type="text" id="tEq" name="tEq" value="99"
     data-val="true" data-val-equalto="必須與上方欄位內容相同"
     data-val-equalto-other="tRange"
     />
    <span data-valmsg-for="tEq"></span>
    </div>
    <div>
    <input type="submit" id="send" value="Send" />
</form>
</body>
</html>

PS: Unobtrusive jQuery Validation一樣可以搭配先前介紹過的"內嵌式錯誤訊息顯示"。


Comments

# by mrkt

這篇雖然說的是jQuery的plug-in, 但我看到的是「Unobtrusive Javascript」這個觀念, 這也是我一直以來的堅持,html與不混合,行為與結構的分離, 頂多只在html裡開個js的入口,傳遞動態產生的參數給js檔使用, 如 Url.Action, 這也是以前同事教我的手法, 希望黑大的這篇文章,可以給更多網頁程式開發人員帶來硬想與改變.

# by Leo

收下了,謝謝黑大。

# by arthas

您好,想請教在此提交表單驗証是使用submit 若是單純想使用button需由哪呼叫呢 XD 感恩 <script type="text/javascript"> $(document).ready(function () { $("#Button1").click(function () { $("#aspnetForm").??(); }); }); </script>

# by Jeffrey

to arthas, 可寫成if ($("#form1").valid()) { ... } PS: 但若是涉及遠端檢核,可能就得處理檢核結果以非同步方式傳回的時間差,寫法要再複雜一點

# by arthas

謝謝黑大的幫忙 使用以上的方式可以檢核了,但有一個部份較為奇怪 使用您的範例 data-valmsg-for 或 data-valmsg-summary 都可正確顯示 但目前要套用的頁面是 masterpage中Content頁就無法顯示 再測試一下把原先要套用的頁面不使用masterpage又能顯示 找了好久不知道什麼原因,不知道該從何下手 所以又來麻煩黑大了

# by Jeffrey

to arthas,使用MasterPage時,input的name及id會被加料,例如: ctl00_ContentPlaceHolder1_TextBox1,若你的data-valmsg-for只寫"TextBox1"就會因為對不上無法顯示。可以透過從Server端將ClientID帶入HTML的做法克服(補充: http://blog.darkthread.net/blogs/darkthreadtw/archive/2007/12/21/1445.aspx),或是用jQuery在載入時搜尋input name動態產生也是一種解法。

# by arthas

感恩 謝謝黑大 確實是masterpage name加料的問題,有參考了您的文章。 但要再標籤內再call javascript 總覺得又繞了一圈 所以現在就直接先寫死像是 <span data-valmsg-for="ctl00$cphMain$TB2"></span> 雖然有點醜 XD 不過覺得有些奇怪jquery.validate.js怎麼是用 name來做data-valmsg-for 而不是用id @@? 不解

# by Jeffrey

to arthas, 我自己的專案裡主要都改用內嵌式顯示(http://blog.darkthread.net/post-2011-07-04-asp-net-mvc-inline-validation-chinese.aspx) ,在makeValidationInline()時會自動找到name補上<span data-valmsg-for="...">,算是逃避了這個惱人問題。

# by Song

您好,想請問是否有使用過於"動態產生"的input type="text"標籤? 我在View中有一個<input type="text">標籤,使用@Html.TextBoxFor方法產生的,所以擁有驗證功能。 若是改手動KEY如: <input type="text" id="txt1" name="[0].txt" data-val="true" data-val-required="txt 欄位是必要項。" /> 也有驗證功能。 但是使用JQuery產生的<input type="text">不能驗證,且有加上如 data_val="true" data_val_required="欄位是必要項。" 就不含有驗證功能了...

# by Jeffrey

to Song, 動態產生輸入元素或修改data-val-*後,需要重新執行$.validator.unobtrusive.parse(...)才會生效。參考: http://stackoverflow.com/questions/14902581/unobtrusive-validation-not-working-with-dynamic-content (也可考慮文中提到的Plugin)

# by Song

To Jeffrey ,問題解決了!謝謝您

Post a comment