活用HTML Custom Attributes
文/黃忠成
起
近年來,在協助多家企業進行ASP.NET專案期間,我撰寫ASP.NET應用程式的風格有了相當大的轉變,以往,我總是以元件為出發點,
提供客戶簡單、易用的元件來解決他們的問題,效果雖然不錯,但對於技術能力較為不足的程式設計師來說,使用元件固然不成問題,
但了解元件的內部、進而掌握她們就有些困難了。
因此,我開始使用一種由來已久的技術,那就是HTML Custom Attributes。
什麼是HTML Custom Attributes
如你所知,在HTML規格中,每個Element都擁有一些Attributes,例如下方的文字輸入框:
<inputname="TextBox1" type="text" id="TextBox1" />
|
其中的type、id、name就是Attributes,HTML解譯器透過解譯這些Attributes,來決定該如何呈現這個HTML Element。很幸運的,
這些Attributes並非固定不變,設計師們可以透過自定Attributes,將一些資訊一併放到HTML Element中,爾後透過Java Script來取得這些值,
進行相對的處理,下面就是這樣一個例子。
HTML
|
<inputname="TextBox1" type="text" id="TextBox1" msg="test" onblur="Test(this)" />
|
.js
|
function Test(sender)
{
alert(sender.getAttribute('msg'));
} |
那這有什麼用呢?不就是將一個特定資訊綁在一個HTML Element上而已嗎?是的,乍看之下的確如此,但是若善用此手法,
我們可以簡化網頁設計工作,將常用的功能撰寫成JavaScript Library,然後透過辨識Attribute的手法,自動的幫設計師完成該做的工作。
接下來我們就以驗證使用者輸入的編號是否重複為例,明確的指出HTML Custom Attributes如何簡化及讓我們的程式碼更加易於閱讀。
驗證編號
驗證使用者輸入的編號是否重複,是多數網頁都需要的步驟,這常用於驗證帳號、驗證客戶編號等功能上。使用者所輸入的資料必須
送回伺服器端,然後由資料庫查詢輸入資料是否重複,在以往,我們多半會選擇以下幾種方法來實作;
- 使用TextBox的TextChanged事件搭配AutoPostBack。
- 使用UpdatePanel搭配TextChanged事件及AutoPostBack。
- 使用AJAX Service Methods,於TextBox的onblur事件回呼伺服器端的Service Methods或是PageMethods。
這三種方法都可以達到預期的效果,第一種是最簡單、但也是最沒有效率的做法,因為TextChanged加上AutoPostBack後,
會引發網頁的刷新動作,在現在這個AJAX盛行的年代,我想已經不會有人再選用這種方法了。
第二種稍微好些,不會引起明顯的網頁刷新動作,但我想你也明白,刷新動作依舊存在,只要有PostBack,就會有一定量的封包送往後端,
唯一不同的是使用者沒感覺到罷了,所以這種方式雖然比第一種好,但也沒好多少。
第三種是目前最建議的做法,這種方式既不會像方法一般引發明顯的網頁刷新動作,也不會像方法二般送出不必要的網路封包,
缺點是寫起來不太容易,下面是這樣的例子。
WebForm1.aspx
|
<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="WebForm1.aspx.cs" Inherits="WebApplication5.WebForm1" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" >
<head id="Head1" runat="server">
<title></title>
<script type="text/javascript" language="javascript">
function CheckSucceededCallback(result, context) {
if (!result) {
alert(context);
}
}
</script>
</head>
<body>
<form id="form1" runat="server">
<asp:ScriptManager ID="ScriptManager1" runat="server" EnablePageMethods="True">
</asp:ScriptManager>
<div>
<asp:TextBox ID="TextBox1"
onblur="PageMethods.CheckRecord(this.value,CheckSucceededCallback,null,'編號重複')"
runat="server">
</asp:TextBox>
<asp:Button ID="Button1" runat="server" Text="Button" />
</div>
</form>
</body>
</html>
|
WebForm1.aspx.cs
|
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
namespace WebApplication5
{
public partial class WebForm1 : System.Web.UI.Page
{
[System.Web.Services.WebMethod]
public static bool CheckRecord(string id)
{
DataSet1TableAdapters.CustomersTableAdapter adapter =
new WebApplication5.DataSet1TableAdapters.CustomersTableAdapter();
return adapter.RecordIsExists(id) == 0;
}
}
}
|
此例是透過ASP.NET AJAX的PageMethods搭配JavaScript來進行驗證,效果不錯,缺點是必須撰寫小量的JavaScript程式碼,
不過此法還是有不方便之處,請注意下面的程式碼片段:
onblur="PageMethods.CheckRecord(this.value,CheckSucceededCallback,null,'編號重複')"
|
在呼叫CheckRecord時,我們將TextBox的值及要驗證失敗的錯誤訊息傳入,爾後驗證失敗時,由CheckSucceededCallback來秀出錯誤訊息,
此法的缺點是,我們沒有地方可以插入當驗證失敗時,把焦點移回原TextBox的程式碼,所以,改成下面這樣才是正確的。
<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="WebForm1.aspx.cs" Inherits="WebApplication5.WebForm1" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" >
<head id="Head1" runat="server">
<title></title>
<script type="text/javascript" language="javascript">
function CallCheck(sender, errMsg) {
PageMethods.CheckRecord(sender.value,CheckSucceededCallback,
null,{element:sender,msg:errMsg});
}
function CheckSucceededCallback(result, context) {
if (!result) {
alert(context.msg);
context.element.focus();
}
}
</script>
</head>
<body>
<form id="form1" runat="server">
<asp:ScriptManager ID="ScriptManager1" runat="server" EnablePageMethods="True">
</asp:ScriptManager>
<div>
<asp:TextBox ID="TextBox1" onblur="CallCheck(this,'編號重複')"
runat="server"></asp:TextBox>
<asp:Button ID="Button1" runat="server" Text="Button" />
</div>
</form>
</body>
</html>
|
在這個例子中,我們用了一個Wrapper Function : CallCheck,並於內以JavaScript Object的方式,將驗證的TextBox及錯誤訊息傳入,
雖然多了一道手續,但也不失為是一個好方法。
更進階的手法,就是透過HTML Custom Attribute來描述錯誤訊息,這種手法會更簡潔,如下所示:
<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="WebForm1.aspx.cs" Inherits="WebApplication5.WebForm1" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" >
<head id="Head1" runat="server">
<title></title>
<script type="text/javascript" language="javascript">
function CheckSucceededCallback(result, context) {
if (!result) {
alert(context.getAttribute("errMsg"));
context.focus();
}
}
</script>
</head>
<body>
<form id="form1" runat="server">
<asp:ScriptManager ID="ScriptManager1" runat="server" EnablePageMethods="True">
</asp:ScriptManager>
<div>
<asp:TextBox ID="TextBox1"
onblur="PageMethods.CheckRecord(this.value,CheckSucceededCallback,null,this)"
errMsg="編號重複" runat="server"></asp:TextBox>
<asp:Button ID="Button1" runat="server" Text="Button" />
</div>
</form>
</body>
</html>
|
你可以明顯的發覺,我們利用了HTML Custom Attributes來描述額外的訊息,於函式中透過getAttribute來取得該訊息,這個例子比起前面幾個來說,
更加的優美,唯一美中不足的是,由於缺少了Wrapper Function,所以PageMethods的呼叫顯得略為冗長。不過由此手法中,我們可以將
Wrapper Function提升為通用化函式,讓程式看起來更簡潔易懂。
CheckId.js
|
function CallCheckFunction(sender) {
if (sender.getAttribute("checkFunction") != null) {
eval("PageMethods." + sender.getAttribute("checkFunction") + "('" + sender.value + "',CheckSucceededCallback,null,sender);");
}
}
function CheckSucceededCallback(result,context) {
if (!result) {
if (context.getAttribute("errorMsg") != null)
alert(context.getAttribute("errorMsg"));
else
alert("error");
context.focus();
}
}
|
Default.aspx
|
<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="Default.aspx.cs" Inherits="WebApplication5._Default" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server">
<title></title>
<script type="text/javascript" language="javascript" src="CheckID.js"></script>
</head>
<body>
<form id="form1" runat="server">
<asp:ScriptManager ID="ScriptManager1" runat="server" EnablePageMethods="True">
</asp:ScriptManager>
<div>
<asp:TextBox ID="TextBox1" checkFunction="CheckRecord"
onblur="CallCheckFunction(this)"
errorMsg="編號重覆" runat="server"></asp:TextBox>
<asp:Button ID="Button1" runat="server" Text="Button" />
</div>
</form>
</body>
</html>
|
這個程式的執行結果與前例完全相同,不同的是可讀性變高了,尤其是在多個驗證區塊情況下更為明顯。
Default.aspx.cs
|
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.Script.Services;
namespace WebApplication5
{
public partial class _Default : System.Web.UI.Page
{
[System.Web.Services.WebMethod]
public static bool CheckRecord(string id)
{
DataSet1TableAdapters.CustomersTableAdapter adapter =
new WebApplication5.DataSet1TableAdapters.CustomersTableAdapter();
return adapter.RecordIsExists(id) == 0;
}
[System.Web.Services.WebMethod]
public static bool CheckEmployeeRecord(string id)
{
DataSet1TableAdapters.EmployeesTableAdapter adapter =
new WebApplication5.DataSet1TableAdapters.EmployeesTableAdapter();
return adapter.RecordExists(int.Parse(id)) == 0;
}
protected void Page_Load(object sender, EventArgs e)
{
}
}
}
|
Default.aspx
|
<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="Default.aspx.cs" Inherits="WebApplication5._Default" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server">
<title></title>
<script type="text/javascript" language="javascript" src="CheckID.js"></script>
</head>
<body>
<form id="form1" runat="server">
<asp:ScriptManager ID="ScriptManager1" runat="server" EnablePageMethods="True">
</asp:ScriptManager>
<div>
<asp:TextBox ID="TextBox1" checkFunction="CheckRecord"
onblur="CallCheckFunction(this)" errorMsg="編號重覆"
runat="server"></asp:TextBox>
<asp:TextBox ID="TextBox2" checkFunction="CheckEmployeeRecord" onblur="CallCheckFunction(this)" errorMsg="員工編號重覆" runat="server"></asp:TextBox>
<asp:Button ID="Button1" runat="server" Text="Button" />
</div>
</form>
</body>
</html>
|
此例尚不完美,因為員工編號是數字,若使用者輸入空白,將會引發例外訊息,改善的方式很簡單,我們再次透過
HTML Custom Attributes,告知CallCheckFunction先幫我們判斷值。
CheckID.js
|
var supsendCheck = false;
function CallCheckFunction(sender) {
if (supsendCheck) {
return;
}
if (sender.getAttribute("valueType") != null &&
sender.getAttribute("valueType") == "int") {
var val = parseInt(sender.value);
if(isNaN(val))
{
alert('格式錯誤!');
sender.focus();
return;
}
sender.value = val;
}
if (sender.getAttribute("checkFunction") != null) {
eval("PageMethods." + sender.getAttribute("checkFunction") +
"('" + sender.value + "',CheckSucceededCallback,null,sender);");
}
}
function CheckSucceededCallback(result,context) {
if (!result) {
supsendCheck = true;
if (context.getAttribute("errorMsg") != null)
alert(context.getAttribute("errorMsg"));
else
alert("error");
context.focus();
}
setTimeout("ResetSupsendCheck()", 0);
}
function ResetSupsendCheck() {
supsendCheck = false;
}
|
Default.aspx
|
<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="Default.aspx.cs" Inherits="WebApplication5._Default" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server">
<title></title>
<script type="text/javascript" language="javascript" src="CheckID.js"></script>
</head>
<body>
<form id="form1" runat="server">
<asp:ScriptManager ID="ScriptManager1" runat="server" EnablePageMethods="True">
</asp:ScriptManager>
<div>
<asp:TextBox ID="TextBox1" checkFunction="CheckRecord"
onblur="CallCheckFunction(this)" errorMsg="編號重覆"
runat="server"></asp:TextBox>
<asp:TextBox ID="TextBox2" checkFunction="CheckEmployeeRecord"
onblur="CallCheckFunction(this)" errorMsg="員工編號重覆" valueType="int"
runat="server"></asp:TextBox>
<asp:Button ID="Button1" runat="server" Text="Button" />
</div>
</form>
</body>
</html>
|
當使用者輸入重複的客戶編號時的畫面如下:
當輸入錯誤格式的員工編號畫面如下:
當輸入重複員工編號畫面如下:
後記
當然,這些例子仍有許多改善空間,本文的目的僅是告知各位讀者,HTML Custom Attrbiutes有許多可利用的空間,透過她們再搭配上掃描整個HTML Tree,
可以讓設計師設幾個Attribute後,就達到意想不到的效果。唯一需注意的是,取Attribute名稱時要特別小心,別與原來的名稱相同,也得避免未來的HTML規格
與你的Attribute重複,當然!HTML已經停在5.0草案很久就是了。
範例下載
沒有留言:
張貼留言