顯示包含「jQuery」標籤的文章。顯示所有文章
顯示包含「jQuery」標籤的文章。顯示所有文章

2012年5月15日星期二

Blogger Recent Posts by jQuery / Ajax / JSONP

之前已經貼過的Recent Comments,當然不會少得Recent Posts。

Demo : http://webapp.heliohost.org/html/blogger_recent_posts_json.htm

使用方法都是一樣很簡單 :
如果你的Template並未加入jQuery,請去設計 / 修改 HTML / 修改範本中,把以下程式碼放在<head>與</head>之間。
<script 
type="text/javascript" 
src="https://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js">
</script>

然後在Blogger新增一個HTML/Javascript小工具,然後把下面的程式碼貼上後,再儲存就完成。


大家使用前,需要去調整一下設定 :
maxcount (line 18) - 最多文章顯示數目。
maxlength (line 19) - 長度多於這個值時,用"..."代替。
feedURL (line 48) - 換上你blogger的ID。

樣式方面,大家可以自行去修改。
  <script type="text/javascript">
   var BloggerFunctions = {
    getPosts : function(feedURL) {
     var paras = {
      alt : 'json-in-script'
     };
     $.ajax({
      url : feedURL,
      type : 'GET',
      dataType : "jsonp",
      success : BloggerFunctions.onGotPostData,
      data : paras
     });
    },

    //Parse the JSON post data returned by the Google Blogger API
    onGotPostData : function(data) {
     var maxcount = 10;
     var maxlength = 20;

     var feed = data.feed;
     var entries = feed.entry || [];
     var title = "";
     var content = "";
     var url = "";
     var published = "";
     var cnt = 0;
     var html = "";

     for(var i = 0; i < entries.length; ++i) {
      var entry = entries[i];
      var title = entry.title.$t;
      //var content = entry.content.$t;
      var url = entry.link[4].href;
      var published = entry.published.$t.substring(0,10);

      html += "<a href='" + url +"'><span style='font-family:Arial;color:#333;font-size:11px;font-weight:bold;'>" + "[" + published + "] " + (title.length > maxlength ? title.substring(0, maxlength) + '...' : title)  + " </span></a><br />";
      cnt++;
      if(cnt == maxcount)
       break;
     }
     var dom = $(html);
     $('#recent_posts').append(dom);
    }
   }

   $(document).ready(function() {
    var feedURL = "http://tatmingstudio.blogspot.com/feeds/posts/default";
    BloggerFunctions.getPosts(feedURL);
   });
  </script>
  <div id="recent_posts"></div>

2012年5月5日星期六

Build Blogger Recent Comments by jQuery / Ajax / JSONP

用上Blogger後,試用一下Google提供的小工具。
使用最近留言(Recent Comments)和最近文章(Recent Posts)幾乎是Blog站的指定部件。

Google提供的小工具是由第三方 - Blogger Buster 製作的。
但可惜廢得可憐,因為是透過iframe呈現,又不能自定樣式,而且最不能接受的是連Trackback都包含在內,又不能把我自己的回應去掉。

Google搜尋,有很多都隨著Google API的更新不能用,或者是我不能理解的做法。

最後當然又是自己動手做,但出師不利,前幾天就遇到問題 - jQuery Cross-Domain Ajax w/XML = NO WAY!

所以唯有轉用JSONP方式,參考了以下文件。
幾經修改過後,製作完成了。
現在大家在右手邊的Sidebar看到的最近留言就是了。
Demo : http://webapp.heliohost.org/html/blogger_recent_comments_json.htm

使用方法很簡單,
如果你的Template並未加入jQuery,請去設計 / 修改 HTML / 修改範本中,把以下程式碼放在<head>與</head>之間。
<script 
type="text/javascript" 
src="https://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js">
</script>

然後在Blogger新增一個HTML/Javascript小工具,然後把下面的程式碼貼上後,再儲存就完成。


大家使用前,需要去調整一下設定 :
maxcount (line 29) - 最多留言顯示數目。
minlength (line 30) - 長度多於這個值才顯示。
maxlength (line 31) - 長度多於這個值時,用"..."代替。
exclude_name (line 32) - 過濾字。
feedURL (line 69) - 換上你blogger的ID。

樣式方面,大家可以自行去修改。
<script type="text/javascript">
   function contains(arr, obj) {
    for(var i = 0; i < arr.length; i++) {
     if(arr[i].toLowerCase() == obj.toLowerCase() && arr[i].length == obj.length)
      return true;
    }
   }
   if( typeof String.prototype.startsWith != 'function') {
    String.prototype.startsWith = function(str) {
     return this.indexOf(str) == 0;
    };
   }
   var BloggerFunctions = {
    getComments : function(feedURL) {
     var paras = {
      alt : 'json-in-script'
     };
     $.ajax({
      url : feedURL,
      type : 'GET',
      dataType : "jsonp",
      success : BloggerFunctions.onGotCommentData,
      data : paras
     });
    },

    //Parse the JSON comment data returned by the Google Blogger API
    onGotCommentData : function(data) {
     var maxcount = 10;
     var minlength = 10;
     var maxlength = 20;
     var exclude_name = new Array("達MiNG");

     var feed = data.feed;
     var entries = feed.entry || [];
     var title = "";
     var content = "";
     var url = "";
     var authorname = "";
     var published = "";
     var cnt = 0;
     var html = "";

     for(var i = 0; i < entries.length; ++i) {
      var entry = entries[i];
      var title = entry.title.$t;
      var content = entry.content.$t;
      var url = entry.link[2].href;
      var authorname = entry.author[0].name.$t;
      var published = entry.published.$t.substring(0,10);
      
      if(!contains(exclude_name, authorname) && !content.startsWith('[...]') && title.length >= 10) {
       html += "<span style='font-family:Arial;color:#333;font-size:11px;font-weight:bold;'>" + "["+ published + "] " 
         +authorname + " 說 : </span><br />" 
         + "<span style='font-family:Arial;color:#666;font-size:11px;'>" 
         + "<a href='" + url + "'>" + (content.length > maxlength ? content.substring(0, maxlength) + '...' : content) + "</a></span>"
         + "<br /><br />";
       cnt++;
      }
      if(cnt == maxcount)
       break;
     }
     var dom = $(html);
     $('#recent_comments').append(dom);
    }
   }

   $(document).ready(function() {
    var feedURL = "http://tatmingstudio.blogspot.com/feeds/comments/default";
    BloggerFunctions.getComments(feedURL);
   });
  </script>
  <div id="recent_comments"></div>

2012年5月3日星期四

jQuery Cross-Domain Ajax w/XML = NO WAY!

今天製作Blogger小工具時,原以為會很順利的。
Google Blogger API Reference Guide看到可以用GET方式得到Comments的XML。

當然二話不說,打開Aptana Studio立即工作。
基於jQuery Ajax,不到一小時已經搞定,在本機試行,沒有問題。
沾沾自喜地心想: (那會難得到我?)


那就把程式碼Copy & Paste至Blogger的HTML/JavaScript小工具,儲存,看一看。
OMFG~竟然什麼都沒有,打開IE9的F12 Developer Tool看看出現大概是這樣的Error。
XMLHttpRequest cannot load XXX.
Origin XXX is not allowed by Access-Control-Allow-Origin.
上Google搜尋一下,原來是Google防止Cross-Domain Request帶出的問題。

根據一些討論區答案,解決方法是轉用JSONP,因為Google API 伺服器不支援CORS (Cross-origin resource sharing)

其實Google API 既有提供JSON,jQuery亦可以解讀,當然問題其實老早已解決。

但我就是有點不服氣,深入理解一下CORS。

按照網上的高手建議,
可以在Http Headers (PHP/ASP.NET)或jQuery Ajax中加入:
Access-Control-Allow-Origin: *
Access-Control-Allow-Methods: GET, POST, PUT, DELETE
Access-Control-Allow-Headers: Authorization

之後又有人建議對jQuery設定
$.support.cors = true;
crossDomain: true;

當然統統試過都沒有用,因為主導權是在Server那邊,要是好像Google般不容許Cross-Domain Request的話,無論如何在Headers出什麼古惑都沒有用。

以下就是我的Blogger Recent Comments的XML版本。
http://webapp.heliohost.org/html/blogger_recent_comments.htm
Save As另存新檔後,在本機用Browser打開,是沒有問題的。

但在線上看,就會出現我說的問題。
當然現在問題已經解決,我已經轉用JSONP方式去實現。
<!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" lang="en">
 <head>
  <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
  <title>blogger_recent_comments</title>
  <script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js"></script>
  <script type="text/javascript">
   function contains(arr, obj) {
    for(var i = 0; i < arr.length; i++) {
     if(arr[i].toLowerCase() == obj.toLowerCase() && arr[i].length == obj.length)
      return true;
    }
   }

   if( typeof String.prototype.startsWith != 'function') {
    String.prototype.startsWith = function(str) {
     return this.indexOf(str) == 0;
    };
   }

   $(document).ready(function() {
    var feedurl = "http://www.blogger.com/feeds/880240131395666753/comments/default";
    var maxcount = 10;
    var minlength = 10;
    var exclude_name = new Array("&#36948;MiNG");

    $.support.cors = true;
    $.ajax({
     type : "GET",
     url : feedurl,
     dataType : "xml",
     crossDomain : true,
     headers : {
      "GData-Version" : "2"
     },
     success : function(responseData, textStatus, jqXHR) {
      var cnt = 0;
      var html = "";
      $(responseData).find('entry').each(function() {
       var title = $(this).find("title").text();
       var content = $(this).find('content').text();
       var url = $(this).find("link[rel='alternate']").attr('href');
       var authorname = $(this).find("author").find('name').text();

       if(!contains(exclude_name, authorname) && !content.startsWith('[...]') && title.length >= 10) {
        html += "<span style='font-family:Arial;color:#333;font-size:11px;font-weight:bold;'>" + authorname + " &#35498; : </span><br />" + "<span style='font-family:Arial;color:#666;font-size:11px;'>" + title + "</span><br /><br />";
        cnt++;
       }
       if(cnt == maxcount)
        return false;
      });
      var dom = $(html);
      $('#recent_comments').append(dom);
     },
     error : function(responseData, textStatus, errorThrown) {
      console.log(textStatus);
     }
    });
   });
  </script>
 </head>
 <body>
  <div id="recent_comments"></div>
 </body>
</html>

2010年8月14日星期六

使用ASP.NET+jQueryUI模擬WordPress的drag&drop widgets

有用過WordPress的網友都會知道WP可以靠Drop&Drop去修改網頁上的Widget位置,這做法有利於可以即時顯示排列結果。

既然是Client-Side的技巧,在ASP.NET上,當然可以做到。
有很多JS Framework都有提供UI Subset去給你直接使用,但我則還是使用jQueryUI去自建,因為明白整個Life cycle,由init到complete,我覺得是很重要的。

jQueryUI方面,你需要用上 Draggable + Droppable + Sortable

ASP.NET方面,當儲存後,下次再進入時,當然要還原之前所設定的,這時候你就需要使用BulletedList Control,因為BulletedList其實就是<ol>或<ul>。

先給大家看看效果,其實暫時未盡完美,因為應該當Drop完成的時候,所複製的<li>element已經要有[Edit]/[Delete] buttons,但大家見到我暫時還需要PostBack才正常顯示是因為未完成。

ASP.NET GridView jQuery Drag & Drop

以前我們要做資料上的Sorting,可能還要用一些箭咀按鈕去一個個排列,但大家應該都用過很多先進的CMS,都會以Drag&Drop形式去操作。
當然這類現代的UI,不會是PHP專美,ASP.NET一樣可以做到。

大家都知道GridView Control 經Browser Render後,其實就是一個HTML Table,所以說穿了這和GridView沒有關係,即使你用Repeater,Listview Control只要ItemTemplate是Table架構就可以做到。

這個Demo我是用ASP.NET 4.0 + jQuery的TableDnD plugin製作的,其實官方網站已有很多Demo示範如何使用,所以我都沒寫 Tutorial 了,只是做一個screencast給大家看看效果,相信已經足夠。

不過大家可能會問影片中的Alert那些字串是什麼,其實就是當onDrop的時候所產生的Array,用途是給你做Ajax update之用。

2010年6月3日星期四

jQuery Ajax配WebService的以外選擇 - Ashx

jQuery用Post方式去呼叫Web Service是最常見的運作方式,不過jQuery除了.post(),還有.get() / .load() / .getScript().getJSON()都是常用的方法。

有點不同的是,後四者一般使用上都是想直接取得字串資料居多,甚至只是單單True或False。(可參考jQuery的Demo)
故然若以最低成本為大前題,使用WebService就不太恰當。
甚至有時候會用Javascript for...loop 去循環callback的話,低成本,快速的程序更為重要。
所以我們便需要用上Ashx。

Ashx只是Extension名,而Ashx正式名稱是Generic handler。
或者可能會問,為什麼不用aspx的response.write去做?
其實的確用response.write是可以做到同一效果,但*.ashx和*.aspx有少少不同:
  1. ashx可以return binary file,例如Image,Zip etc...
  2. ashx並不需要經過aspx的Page Cycle,例如Page_Init , Page_Load etc...
  3. ashx可以在web.config設定http request的處理方式

特別是第二,三點,所以以速度來說,ashx是比aspx更快,更省CPU資源。

以下是ashx示範,是由MSSQL取得 Image Field 並呈現在瀏覽器上,如果文字處理就更簡單,Visual Studio的ashx文件,預設的就是Hello World示範。

<%@ WebHandler Language="VB" Class="ShowStudentImage" %>

Imports System
Imports System.IO
Imports System.Data
Imports System.Data.SqlClient
Imports System.Web

Public Class ShowStudentImage : Implements IHttpHandler
Dim StudentID As String = ""
Private Property SID() As String
Get
Return StudentID
End Get
Set(ByVal value As String)
StudentID = value
End Set
End Property

Public Sub ProcessRequest(ByVal context As HttpContext) Implements IHttpHandler.ProcessRequest
If context.Request.QueryString("StudentID") IsNot Nothing Then
SID = context.Request.QueryString("StudentID")
Else
Throw New ArgumentException("No parameter specified")
End If

context.Response.ContentType = "image/jpeg"
Dim strm As Stream = ShowEmpImage(SID)
Dim buffer As Byte() = New Byte(4095) {}
Try
Dim byteSeq As Integer = strm.Read(buffer, 0, 4096)
While byteSeq > 0
context.Response.OutputStream.Write(buffer, 0, byteSeq)
byteSeq = strm.Read(buffer, 0, 4096)
End While
Catch
context.Response.ContentType = "image/gif"
context.Response.WriteFile("images/Transparent.gif")
End Try
End Sub

Public Function ShowEmpImage(ByVal StudentID As String) As Stream
Dim connection As New SqlConnection(ConfigurationManager.ConnectionStrings("ClientsConnectionString").ConnectionString)
connection.Open()
Dim sql As String = "SELECT Photo FROM Student WHERE StudentID = @SID"
Dim cmd As New SqlCommand(sql, connection)
cmd.CommandType = CommandType.Text
cmd.Parameters.AddWithValue("@SID", Integer.Parse(StudentID))
Dim img As Object = cmd.ExecuteScalar()
Try
Return New MemoryStream(DirectCast(img, Byte()))
Catch
Return Nothing
Finally
connection.Close()
End Try
End Function

Public ReadOnly Property IsReusable() As Boolean Implements IHttpHandler.IsReusable
Get
Return False
End Get
End Property
End Class

2010年6月2日星期三

JQuery UI Tabs size問題

今天用jQueryUI的Tabs出現一個問題,就是Tabs size非常大。

最奇怪的是,我引用官方的Demo code都出現這情況,難道連官方的都出錯?

查明後,原因是Tabs的font size是由.ui-helper-reset去控制,而.ui-helper-reset的font-size是100%,所以如果你的Tabs並沒有放置在其他已Define font-size的Container (例如Div),那就會出現這個問題。
所以解決方法是把Tabs的<div id="tabs">...</div>放在一個container中,問題就得以解決。

正確HTML Code如下
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" />
<link type="text/css" href="css/redmond/jquery-ui-1.8.1.custom.css" rel="stylesheet" />
<script type="text/javascript" src="js/jquery-1.4.2.min.js"></script>
<script type="text/javascript" src="js/jquery-ui-1.8.1.custom.min.js"></script>
<title></title>
<script type="text/javascript">
$(function() {
$("#tabs").tabs();
});
</script>
</head>
<body>
<div id="container" style="font-size:12px">  <!-- important -->
<div id="tabs">
<ul>
<li><a href="#tabs-1">First</a></li>
<li><a href="#tabs-2">Second</a></li>
<li><a href="#tabs-3">Third</a></li>
</ul>
<div id="tabs-1">
Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor
incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud
exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.</div>
<div id="tabs-2">
Phasellus mattis tincidunt nibh. Cras orci urna, blandit id, pretium vel, aliquet
ornare, felis. Maecenas scelerisque sem non nisl. Fusce sed lorem in enim dictum
bibendum.</div>
<div id="tabs-3">
Nam dui erat, auctor a, dignissim quis, sollicitudin eu, felis. Pellentesque nisi
urna, interdum eget, sagittis et, consequat vestibulum, lacus. Mauris porttitor
ullamcorper augue.</div>
</div>
</div>
</body>
</html>

2009年11月7日星期六

jQuery - 當Javascript Ajax遇上Loop

之前已經略略打過一篇jQuery 的Async和sync的問題:
jQuery – Why Ajax return blank data?

近日我再次遇上近似的問題,我把一段jQuery.ajax 放在一段For...Loop中運行,發現同樣會出現同步問題。
再次上網查證一下,發現原來這不是jQuery的問題,問題在於Javascript原生的XMLHttpRequest和Synchronous問題。

出現問題的程式碼如下:
ASP.NET 2.0 Web Service :
<WebMethod()> _
Public Function returnString(ByVal inputString As String) As String
Return inputString
End Function

網頁上的jQuery Ajax CallBack :
<script type="text/javascript">
$(document).ready(function () { 
for (var i = 1 ;i <= 10;i++)
{
var Options= {
type: "POST",
url: "webservice/DataService.asmx/returnString",
data:'{ inputString: ' + i +'}',
async: "true",
contentType: "application/json; charset=utf-8",
dataType: "JSON",
success: function(response)  { alert(response);  } 
} 
$.ajax(Options); 
}
});
</script>

運行程式後,得出的alert不是順序的12345678910,有可能是4231789...或者9714523。
最後把async設成false就可以解決問題,但是運行時,Browser會呈現硬直狀態。
解決方法雖然是找到了,但是為什麼會這樣?
可以睇睇國外高手的Blog文:
Ajax and javascript don't use threads
Ajax, javascript and threads : the final truth

睇過之後,不難理解為什麼會出現這個同步問題。

2009年8月9日星期日

jQuery - Why Ajax return blank data?

jQuery的Ajax很好用,幾乎我已經以jQuery取代Microsoft的Ajax,但使用jQuery的Ajax Call Back要注意 Asynchronous (同步或非同步) 問題。
以這個典型例子:
$("InputButton").click(function () { 
$.ajax({
type: "POST",
url: "webservice/DataService.asmx/GetAccount",
data: "{AccountNo: '123' }",
contentType: "application/json; charset=utf-8",
dataType: "json",
success: function(msg){ alert(msg); }
});
});

上面的例子是沒有問題的,但如果把Ajax部份寫成Function,就會出現CallBack空白的Data:
$("InputButton").click(function () { 
alert(GetAccountName('123'));
});

function GetAccountName(Code)
{
var r = '';
$.ajax({
type: "POST",
url: "webservice/DataService.asmx/GetAccount",
data: "{AccountNo: '" + Code +"' }",
contentType: "application/json; charset=utf-8",
dataType: "json",
//async: false, //假設沒有這行
success: function(msg){
if (msg.length > 0) { r = msg; }
}
});
return r;
}

解決的方法就是加入這一行
async: false,

用意是需要等待jQuery 取得Data後才運行下一個動作。

2009年5月11日星期一

jQuery + Visual Studio IntelliSense Error 解決方法

今天公司機的Visual Web Developer 2008出現:
"更新 JScript IntelliSense 時發生錯誤"

Documents\Visual Studio 2008\WebSites\{project}\js\jquery-1.3.2.min.js: 
Object doesn't support this property or method @ 18:9345..."

2009510_5.png

雖然我已經有安裝HotFix,但都沒有效。

jQuery + ASP.NET Web Service : data.d or not d?

之前講過,當使用jQuery CallBack ASP.NET Web Service後,假設Data部份,我用"response"做名稱的話,我便可以用each()方法去extract "response"的values.

ASP.NET 2.0的做法:
$.each(Sys.Serialization.JavaScriptSerializer.deserialize(response), function(key, item) {
alert(item.ProductModel + ' / ' + item.Description);
});

以上做法本應是沒有錯誤的,但如果是ASP.NET 3.5的情況下,便會出現錯誤 :
"Sys.ArgumentTypeException: Object of type 'Object' cannot be converted to type 'String'.
Parameter name: data"

即使你使用的不是deserialize()方法,而是eval(),都會出現undefined.
2009510_1.png

這問題搞了我兩天,因為我公司是用ASP.NET 2.0平台,而我家中是3.5平台。程式碼肯定就沒有問題,因為Web Service部份已經設定return成string,而一直都不為意3.5的Web Service會override return的DataType。幸好在Firebug發現了奇妙的"d" :

ASP.NET 2.0, 在Firebug顯示的return values (response) , 在IE8的Develper Tools顯示DataType為String。
2009510_2.png
2009510_3.png

ASP.NET 3.5, 在Firebug顯示的return values (response) , DataType為Object,底下還有"d"的member。
2009510_4.png

所以最後觸發到我用"response.d"去得出string的值。上網找找"d"是什麼東西,原來也有人遇到同樣問題。
A breaking change between versions of ASP.NET AJAX | Encosia
特別要看看這個comment,完美解釋"d"是什麼東西:

2009年5月9日星期六

jQuery教學(4) - jQuery 使用 ASP.NET Web Service 示範

上次講解jQuery如何對一些Web Service / API進行deserialize Json , 今次我會承接下去示範如何用jQuery去呼叫ASP.NET 3.5 Web Service,並利用jQuery UI做出很正的UI。其實除了官方的jQuery UI,還有很多不同的jQuery Plugin都是比原本ASP.NET的Web Control更漂亮。而且由於ASP.NET MVC沒有Server Control,所以學習使用一些JS Framework的UI以應付未來的Web開發是必須的。

Web Service方面是使用LINQ-to-SQL,並把DataTable Serialize後回傳給jQuery。
Database方面則使用AdventureWorks Sample DB。
下載:AdventureWorks Sample DB
下載:jQuery UI
下載:範例的Source Code

運行時的畫面:
20090905_jqueryui.png

程式碼方面其實不複雜,我還是直接貼上Source Code,註解一下比較重要的部份。

Web Service部份:
using System;
using System.Collections.Generic;
using System.Data;
using System.Data.SqlClient;
using System.Web;
using System.Web.Services;

[WebService(Namespace = "http://tempuri.org/")]
[WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
[System.Web.Script.Services.ScriptService]
public class getDataTable : System.Web.Services.WebService {
    [WebMethod]
    public string getProducts()
    {
        DataTable dt = new DataTable();
        using (SqlConnection MyConnection = new SqlConnection("Data Source=LocalHostSQLEXPRESS;Initial Catalog=AdventureWorks;Integrated Security=SSPI;"))
        {
            MyConnection.Open();
            string MySelect = "SELECT TOP 20 [ProductModel] , [Description] FROM Production.vProductAndDescription WHERE CultureID='en' GROUP BY [ProductModel] , [Description] ";
            SqlCommand MyCommand = new SqlCommand(MySelect, MyConnection);
            using (SqlDataAdapter da = new SqlDataAdapter(MyCommand))
            { da.Fill(dt); }
        }
        return GetJson(dt);
    }

    public string GetJson(DataTable dt)
    {
        System.Web.Script.Serialization.JavaScriptSerializer serializer = new System.Web.Script.Serialization.JavaScriptSerializer();
        List<Dictionary<string, object>> rows = new List<Dictionary<string, object>>();
        Dictionary<string, object> row;
        foreach (DataRow dr in dt.Rows)
        {
            row = new Dictionary<string, object>();
            foreach (DataColumn col in dt.Columns)
            { 
                row.Add(col.ColumnName, dr[col]);
            }
            rows.Add(row);
        }
        return serializer.Serialize(rows);
    }

}

頁面的jQuery 使用Ajax CallBack 部份:
    <script type="text/javascript">

        // Init jQuery UI
        $(function init() {
            // Initialize Accordion
            $("#accordion").accordion({ header: "h3" });
            // Initialize Slider
            $('#slider').slider({ min: 1, max: 20,
                slide: function (event, ui) { $('#limit').html(ui.value); }
            });
        });

        $(document).ready(function () {
            // jQuery Ajax CallBack
            $("#lnkGetProducts").click(function () {
                // Setup the Options for jQuery Ajax
                var Options = {
                    type: "POST",
                    url: "getDataTable.asmx/getProducts", // invoke web service
                    data: "{dummyKey: '" + "dummyVal" + "'}", // dummyKey and dummyVal can be remove.
                    contentType: "application/json; charset=utf-8",
                    dataType: "json",
                    success: function (response) {
                        if (response != "") {
                            var s = '<div id="accordion">';
                            var limit = $("#limit").html(); // the slider value to limit result output.
                            $.each(Sys.Serialization.JavaScriptSerializer.deserialize(response.d), function (key, item) {
                                s += '<div>';
                                s += '<h3><a href="#">' + item.ProductModel + '</a></h3>';
                                s += '<div>' + item.Description + '</div>';
                                s += '</div>';
                                if (key == limit - 1) return false; // if reach limit , return and exit.
                            });
                            s += '</div>';
                            $('#container').html(s); // fill s into container div
                            init(); // Initialize jQuery UI again when the DOM is changed.
                        }
                    }
                };
                $.ajax(Options); // Execute and CallBack
            });

        });
    </script>

就整份文件來說,比較難理解的或者是Javascipt中Line 27的"response.d",或者你會想問"d"這個member是什麼呢?
其實有"d"與否是關係到ASP.NET 2.0 和3.5之間的Web Service回傳的DataType問題。
將會在下一篇文章講解。
如果你是ASP.NET 2.0的話,就要刪除".d",相反是3.5的話就要保留。要不然會發生Object 和 String的DataType轉型錯誤。

2009年5月6日星期三

jQuery教學(3) - Deserialization of Json

Json (讀音-Jason,不要讀做J-S-On啊...http://en.wikipedia.org/wiki/JSON)
如果要用jQuery以Ajax存取Database,以ASP.NET為例,無論是使用Page Method或者Web Service時,普遍都是Return Json格式的。與XML相比,Json格式沒有Tag,大大減小資料的大小。使用逗號分隔資料;冒號區分Keys/Values,可讀性不遜於XML。

Json格式的例子:
[
   {"Name":"Visual Studio Express","Type":"Dev IDE","License":"Free"},
   {"Name":"WordPress","Type":"Blog System","License":"Open Source"},
   {"Name":"WinRAR","Type":"Archive","License":"Commercial"},
   {"Name":"iTunes","Type":"Music Player","License":"Free"},
   {"Name":"FileZilla","Type":"FTP Tool","License":"Open Source"}
]

甚至部份網上服務的API,除XML格式外,還會直接提供CallBack Json格式。目的就是盡量減小Data的大小,而且可以直接讓很多不同的Javascript Framework讀取。
以Flickr相簿為例 :
API Service Url : http://api.flickr.com/services/feeds/photos_public.gne?tags=hongkong&tagmode=any&format=json

用Notepad打開photos_public.gne就可以見到是Json字串。

Deserialize
要解開Json格式資料,就要進行Deserialize把Json轉至類似C#的Enumeration Types,不同的是Json格式的Key是可以重複的。

可能你會發現jQuery有serialize() Method,但沒有deserialize(),原因不是作者懶...而是沒有需要。
因為要做Deserialize可以有三個方法(當然還有很多,不能盡錄),分別是:
Javascript本身的eval() / Json官方的json_parse() 以及 Microsoft ASP.NET AJAX Library下的Sys.Serialization.JavaScriptSerializer.deserialize

Eval()
<html>
<head>
    <title></title>
    <script type="text/javascript" src="http://code.jquery.com/jquery-latest.min.js"></script>
    <script type="text/javascript">
        $(document).ready(function () {
            var $strJson = '[';
            $strJson += '{"Name":"Visual Studio Express","Type":"Dev IDE","License":"Free"},';
            $strJson += '{"Name":"WordPress","Type":"Blog System","License":"Open Source"},';
            $strJson += '{"Name":"WinRAR","Type":"Archive","License":"Commercial"},';
            $strJson += '{"Name":"iTunes","Type":"Music Player","License":"Free"},';
            $strJson += '{"Name":"FileZilla","Type":"FTP Tool","License":"Open Source"}';
            $strJson += ']';
            $.each(eval($strJson), function (key, item) {
                $("body").append(item.Name + ' / ' + item.Type + ' / ' + item.License + '<br />');
            });
        });
    </script>
</head>
<body>
    <!--
Output Result:
Visual Studio Express / Dev IDE / Free
WordPress / Blog System / Open Source
WinRAR / Archive / Commercial
iTunes / Music Player / Free
FileZilla / FTP Tool / Open Source
-->
</body>
</html>

json_parse()
<html>
<head>
<title></title>
<script type="text/javascript" src="http://code.jquery.com/jquery-latest.min.js"></script>
<script type="text/javascript" src="http://www.json.org/json_parse.js"></script>
<script type="text/javascript">
    $(document).ready(function () {
        var $strJson = '[';
        $strJson += '{"Name":"Visual Studio Express","Type":"Dev IDE","License":"Free"},';
        $strJson += '{"Name":"WordPress","Type":"Blog System","License":"Open Source"},';
        $strJson += '{"Name":"WinRAR","Type":"Archive","License":"Commercial"},';
        $strJson += '{"Name":"iTunes","Type":"Music Player","License":"Free"},';
        $strJson += '{"Name":"FileZilla","Type":"FTP Tool","License":"Open Source"}';
        $strJson += ']';
        $.each(json_parse($strJson), function (key, item) {
            $("body").append(item.Name + ' / ' + item.Type + ' / ' + item.License + '<br />');
        });
    });
</script> 
</head>
<body>
<!--
Output Result:
Visual Studio Express / Dev IDE / Free
WordPress / Blog System / Open Source
WinRAR / Archive / Commercial
iTunes / Music Player / Free
FileZilla / FTP Tool / Open Source
-->
</body>
</html>

deserialize()
<%@ Page Language="VB" AutoEventWireup="false" CodeFile="Default.aspx.vb" Inherits="_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 id="Head1" runat="server">
    <title></title>
    <script type="text/javascript" src="http://code.jquery.com/jquery-latest.min.js"></script>
    <script type="text/javascript">
        $(document).ready(function () {
            var $strJson = '[';
            $strJson += '{"Name":"Visual Studio Express","Type":"Dev IDE","License":"Free"},';
            $strJson += '{"Name":"WordPress","Type":"Blog System","License":"Open Source"},';
            $strJson += '{"Name":"WinRAR","Type":"Archive","License":"Commercial"},';
            $strJson += '{"Name":"iTunes","Type":"Music Player","License":"Free"},';
            $strJson += '{"Name":"FileZilla","Type":"FTP Tool","License":"Open Source"}';
            $strJson += ']';
            $.each(Sys.Serialization.JavaScriptSerializer.deserialize($strJson), function (key, item) {
                $("body").append(item.Name + ' / ' + item.Type + ' / ' + item.License + '<br />');
            });
        });
    </script>
</head>
<body>
    <form id="form1" runat="server">
    <div>
        <!--Place a ScriptManager for Sys NameSpace-->
        <asp:ScriptManager ID="ScriptManager1" runat="server" />
    </div>
    </form>
</body>
</html>

你可以見到其實三種方法使用上分別不大,唯一注意的是如果使用Microsoft的,緊記放上ScriptManager。它會自動load入ASP.NET AJAX library 和 script files。

如果要我說,會用那種方法。我想比較小的資料會直接用eval()就可以了。如果是很多資料的話,會用Microsoft的,因為Microsoft的Ajax library原本是給Webforms上的Ajax使用。而Ajax control Toolkit都是使用它,所以處理大量資料下的效能,應該會較優勝。

2009年4月26日星期日

jQuery教學(2) - GridView/Table的slide/fade/animate/Class效果

jQuery的效果其實並不算很多,除了基本的Show/Hide/Toggle,就還有slide / fade / animate 三種。這次依然是用GridView Table做示範,但其實這幾種效果都可以用於其他的HTML element之上,另外還有Add/Remove Class的應用。
有需要可先看看上一次教學 : jQuery教學(1) - GridView/Table中使用Selectors
大家可以先試試效果 :


1. Click一下Row,會把整列Highlight起來。
2. 選取Row後,按Delete按鈕會把Row FadeOut。
3. Double click 藍色的格子會動畫式把文字放大。
4. Double click 粉紅色的格子會把文字摺疊起來。

我想只有toggleClass才需要用一點點文字表達,其他的,相信只放上代碼大家都能看得懂。

toggleClass
1.用Selector+filter把整個Table的cell return,filter的意義並不是把符合的去掉,而是相反地return給jQuery API。
2.再remove所有row的的Class:currRow,確保所有Row都沒有Highlight。
3.最後才把click() event中的row加上currRow class達至選取效果。
    <script type="text/javascript">
        //HighLight Row after mouse click
        $('#gridtable tr').filter(function () {
            return $('td', this).length && !$('table', this).length
        }).click(function () {
            $('#gridtable tr').removeClass("currRow");
            $(this).toggleClass('currRow');
        });
    </script>

fade
    <script type="text/javascript">
        //delete row
        $("#del").click(function () {
            var hideRows = $("#gridtable tr").hasClass("currRow");
            if (hideRows == true) {
                $("#gridtable tr.currRow td").fadeOut(1000);
            }
        });
    </script>

animate & slide
    <script type="text/javascript">
        //animate & slide 
        //Only td with div inside will be select.
        //title="ani" is the flag for animate.
        $('#gridtable tr td div').dblclick(function () {
            if ($(this).attr('title') == 'ani') {
                $(this).animate({
                    width: "100%",
                    opacity: 0.4,
                    marginLeft: "0in",
                    fontSize: "50px",
                    borderWidth: "10px"
                }, 1500);
            } else {
                $(this).slideToggle("slow");
            }
        });
    </script>

大家可見其實,透過jQuery去做這些效果並不複雜。其實WordPress中的Admin頁上,或者很多Web 2.0網站上,大家都看到很多類似的效果。

完整的網頁HTML碼:
<!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>
    <title>亞特蘭提斯 Net Atlantis - jQuery教學(2) - GridView/Table的slide/fade/animate效果</title>
    <script type="text/javascript" src="http://code.jquery.com/jquery-latest.min.js"></script>
    <script type="text/javascript">
        $(document).ready(function () {
            //HighLight Row after mouse click
            $('#gridtable tr').filter(function () {
                return $('td', this).length && !$('table', this).length
            }).click(function () {
                $('#gridtable tr').removeClass("currRow");
                $(this).toggleClass('currRow');
            });

            //delete row
            $("#del").click(function () {
                var hideRows = $("#gridtable tr").hasClass("currRow");
                if (hideRows == true) {
                    $("#gridtable tr.currRow td").fadeOut(1000);
                }
            });

            //animate & slide
            //Only td with div inside will be select.
            //title="ani" is the flag for animate.
            $('#gridtable tr td div').dblclick(function () {
                if ($(this).attr('title') == 'ani') {
                    $(this).animate({
                        width: "100%",
                        opacity: 0.4,
                        marginLeft: "0in",
                        fontSize: "50px",
                        borderWidth: "10px"
                    }, 1500);
                } else {
                    $(this).slideToggle("slow");
                }
            });

        });
    </script>
    <style type="text/css">
        body
        {
            font: small arial;
        }
        tr:first-child
        {
            background-color: #F5F5DC;
        }
        tr:first-child:hover
        {
            background-color: #F5F5DC;
        }
        tr:hover
        {
            background-color: #E8FFEC;
            cursor: pointer;
        }
        td
        {
            border: #ccc 1px solid;
            text-align: center;
            width: 100px;
        }
        td:hover
        {
            background-color: #E8FFEC;
            border: #666 1px solid;
            width: 100px;
            cursor: pointer;
        }
        .currRow
        {
            background-color: #F0BD00;
            cursor: pointer;
        }
    </style>
</head>
<body>
    <table id="gridtable">
    <tr>
    <th>Product id</th><th>Console Name</th><th>Colour</th>
    </tr>
    <tr>
    <td><div title="ani"  style="background-color:#ADD8E6">1</div></td><td>Play Station 3</td><td>Black</td>
    </tr>
    <tr>
    <td>4 </td><td>Xbox 360</td><td><div title="ani" style="background-color:#ADD8E6">White</div></td>
    </tr>
    <tr>
    <td>11 </td><td><div title="slide" style="background-color:#FFB6C1">NDSL</div></td><td>Red</td>
    </tr>
    <tr>
    <td>18</td><td>PSP</td><td><div title="slide" style="background-color:#FFB6C1">Silver</div></td>
    </tr>
    <tr>
    <td>26</td><td><div title="ani" style="background-color:#ADD8E6">Wii</div></td><td>White</td>
    </tr>
    </table>
    <button id="del" type="button">Delete Selected Row</button>
    <button id="reload" type="button" onclick="window.location.reload()">Reload Table</button>
</body>
</html>

2009年4月21日星期二

jQuery教學(1) - GridView/Table中使用Selectors

這段時間在公司比較空閒,做了幾個Sample,順道放上來介紹一下jQuery。
我會講下jQuery中的Core Function / Event /Selectors / Effect的用法,和Ajax如何Call ASP.NET Web Service/Page Method,以及如何對JSON做Serialization 和 Deserialization。

其實大家都知道ASP.NET的GridView Control在Browser顯示是以Table形式顯示,唯一特別的地方是GridView的Header的Cell是用<th>表示,而Row的Cell則用<td>,所以GridView和一般HTML是可以混為一談的。

有時候我們會想知道Table中某一行某一格的Value是什麼,又或者想因應不同情況要Apply CSS去其中一格,當然在Server-Side那邊,在Row Collection中找尋是很容易。但在Client-Side用上JavaScript,要在DOM中找尋Table再找尋特定的Cell都會很麻煩。

先看看Demo :


jQuery的Selectors最強的地方就是可以在整個HTML文件中穿梭,即使遇上很複雜的Table,只要是正確的DOM文件,你亦可以很容易找到你想要的,而jQuery的Selectors有很多種運作模式,務求用最少的Code去做最多的工作。

此文就以這個Table做範例,有三個Column,五個Row去示範如何用Selector去取得Cell的Column index和Row Index,同時亦相反地用Index去取得Cell中的Value。當然就如之前所講,此Table等同Browser中的GridView Control。

Table的HTML :
<!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>
</head>
<body style="font-family: Tahoma; font-size: 11px;">
    <table id="gridtable">
        <!--&#32202;&#35352;&#21152;&#19978;id-->
        <tr>
            <th>Product id</th>
            <th>Console Name</th>
            <th>Colour</th>
        </tr>
        <tr>
            <td>1</td>
            <td>Play Station 3</td>
            <td>Black</td>
        </tr>
        <tr>
            <td>4</td>
            <td>Xbox 360</td>
            <td>White</td>
        </tr>
        <tr>
            <td>11</td>
            <td>NDSL</td>
            <td>Red</td>
        </tr>
        <tr>
            <td>18</td>
            <td>PSP</td>
            <td>Silver</td>
        </tr>
        <tr>
            <td>26</td>
            <td>Wii</td>
            <td>White</td>
        </tr>
    </table>
</body>
</html>

首先第一步當然就是jQuery的js檔。其實大家可以直接引用Google-Host的jQuery或者jQuery官網上的。
<script type="text/javascript" src="http://code.jquery.com/jquery-latest.min.js"></script>

再來就是Javascript部份,我要做的是,當Click每一個Cell時,會alert出三個Index值,分別是TD在Table中順序的Index / TD在那個TR的Column Index和Row在Table中的Row Index。
<script type="text/javascript">
        $(document).ready(function () {
            $('#btnGetCell').click(function () {
                var col = $('#colindex').val();
                var row = parseInt($('#rowindex').val()) + 1;
                var result = $('#gridtable tr:eq(' + row + ') td:eq(' + col + ')').html();
                alert('value is : ' + result);
            })

            $('#gridtable tr td').click(function () {
                alert('cell index in table : ' + $('td').index(this));
                alert('cell index in selected row : ' + this.cellIndex);
                alert('row index in grid : ' + (this.parentNode.rowIndex - 1));
            })
        });
    </script>

第二行的$(document).ready是很重要的,因為這代表了Page_Load的Event,把底下的function載入。
只需要定義一次就可以,可以如上面一樣,同時把多個不同的function放進ready event之中。

第一個btnGetCell click的Function就是使用Selector取得Index,而Selector都是由'$'開始,例如$('#btnGetCell') , '#'之後的就是element的ID,之後的#colindex和#rowindex加val()就是要取得TextBox中的value。而val(),html(),text()三者是不同的, val()是取得如input當中的value,而html()則是innerHTML,例如<td>123</td>中的'123,而text()則同html()相似,但會省略其他的Tag,例如<p><b>Hello</b> World</p>,當我使用alert($("p").text()); 便會出現Hello World,而不是<b>Hello</b> World

值得一提的就是'eq',
一般情況下Selector可以是階層式運作,例如我要Table中的TR中的TD,我可以寫成$('#gridtable tr td'),但一個TR中會有多個TD,這時就要寫成$('#gridtable tr td:eq(2)')。
2代表Index,是Zero-Base,亦即是第三個TD。

至於第二個Function,中的this代表TD的Collection,大家可以奇怪為什麼會Skip了Header的Cell。其實這就是Selector其中一種的工作方式,因為如之前所說Header是TR->TH ,而不是TR -> TD。所以在第二個function中,一切Header的th cell都會省卻。

而this.cellIndex和this.parentNode.rowIndex 則是HTML DOM properties,與jQuery無關,但亦同時顯示到jQuery利害之處就是能配合傳統的Javascript和HTML工作。

下次,我會講下使用jQuery的animate effect和slide effect。

完整的網頁HTML碼:
<!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>
    <title>亞特蘭提斯 Net Atlantis - jQuery教學(1) - GridView/Table中使用Selectors</title>
    <script type="text/javascript" src="http://code.jquery.com/jquery-latest.min.js"></script>
    <script type="text/javascript">
        $(document).ready(function () {

            $('#btnGetCell').click(function () { //Event Helpers
                var col = $('#colindex').val();
                var row = parseInt($('#rowindex').val()) + 1;
                var result = $('#gridtable tr:eq(' + row + ') td:eq(' + col + ')').html();
                alert('value is : ' + result);
            })

            //http://www.quirksmode.org/dom/w3c_html.html 
            //http://www.quirksmode.org/dom/tests/tablesIndices.html
            $('#gridtable tr td').click(function () {
                alert('cell index in dom : ' + $('td').index(this));
                alert('cell index in selected row : ' + this.cellIndex);
                alert('row index in grid : ' + (this.parentNode.rowIndex - 1));
            })

        }); //<--End ready event
    </script>
    <style type="text/css">
        body
        {
            font: small arial;
        }
        tr:first-child
        {
            background-color: #F5F5DC;
        }
        tr:first-child:hover
        {
            background-color: #F5F5DC;
        }
        tr:hover
        {
            background-color: #E8FFEC;
            cursor: pointer;
        }
        td
        {
            border: #ccc 1px solid;
            text-align: center;
            width: 100px;
        }
        td:hover
        {
            background-color: #E8FFEC;
            border: #666 1px solid;
            width: 100px;
            cursor: pointer;
        }
    </style>
</head>
<body>
    <table id="gridtable">
    <tr>
    <th>Product id</th><th>Console Name</th><th>Colour</th>
    </tr>
    <tr>
    <td>1</td><td>Play Station 3</td><td>Black</td>
    </tr>
    <tr>
    <td>4 </td><td>Xbox 360</td><td>White</td>
    </tr>
    <tr>
    <td>11 </td><td>NDSL</td><td>Red</td>
    </tr>
    <tr>
    <td>18</td><td>PSP</td><td>Silver</td>
    </tr>
    <tr>
    <td>26</td><td>Wii</td><td>White</td>
    </tr>
    </table>
    column index :<input id="colindex" type="text" size="1" value="1" />
    row index :<input id="rowindex" type="text" size="1" value="1" />
    <button id="btnGetCell">Get Cell Value</button>
</body>
</html>

2009年4月17日星期五

Essential PHP Techniques for Web Designer and Developers

睇完呢個網Desizn Tech , 介紹左廿多種PHP的東西是Web Designer和Web Developer都要識的,當然無所謂絕對。但到了今天,Web Application實在五花百門,無論是設計或者程式部份已經比以前複雜很多,要學的東西自然會多。
雖然近期ASP.NET MVC 1.0已推出,Silverlight 3.0又差不多說要Release。但我還是認為,我把時間花在PHP和jQuery會更幫到我工作。而jQuery是我覺得Designer或Developer都要學習的技術。

PHP
200px-php-logo.png
自從上年10月開始,工作轉寫PHP後,我好認同PHP是種好Flexible,好岩入門甚至Designer去學的一種語言,除左免費同資源多之外,PHP本身語法實在容易,假如你完全未接觸過Programming,只要你每天花幾小時去學,幾星期內已經學有所成。其實外國很多Web Designer都能寫基本的PHP,即使日後有機會接觸到PHP或者轉職做Programmer時,都不會臨急抱佛腳。其實PHP+MySQL拎資料都只是10行不到的Syntax,何不一試。

jQuery
logo_jquery.png
其實接觸jQuery都是這幾個月的事,看到連Microsoft都支持,而Visual Studio 2008更native support jQuery,用左幾個月,現在差不多9成工作都依賴jQuery。jQuery的Selectors實在太強,配合ASP.NET的GridView或Repeater使用都十分方便。其次就Ajax + Web Service / Page Method都是十分好用。除非必要,如果唔係我都會用jQuery的Ajax多過Microsoft的Ajax。jQuery其中一個好用亦是我最常用之處是Selectors配合eq可以十分輕易取得Dom中的資料,例如一個Grid模式的Table,要取得Table中第幾個TR中第幾TD中的innerHTML,jQuery幾隻字已經做到。在Web Designer方面,jQuery除左本身的effects有很多PlugInUI可以使用。幾乎不用什麼Java Script基礎都可以用得很好。在Javascript Framework來說,jQuery是最容易使用,亦是Documentation最齊,Sample最易明白的。