顯示具有 jQuery 標籤的文章。 顯示所有文章
顯示具有 jQuery 標籤的文章。 顯示所有文章

2014年4月4日 星期五

jQuery 對 checkbox 的操作( $('#').attr('checked') 無法正確取得結果 )

jQuery大概是目前最受歡迎的 Javascript Framework,在網頁中操作 checkbox 也是常有的事,那在 jQuery中要如何操作 checkbox 呢,上網可能搜到上百個解答,但許多都是相互抄來的,您可能發現很多解答告訴我們用   $("#check").attr("checked")  來判斷是否被勾選,但結果與「想像」的不一樣,因為 $("#check").attr("checked")  並不會傳回 true 或 false,而是傳回 checked 這個屬性的值,詳解如下:
要如何判斷checkbox 是否已被勾選?
$("#check").attr("checked")  很難正確判斷是否真的被勾選
如果 <input  type="checkbox" id="check" ...>   有勾選,會傳回 "checked"
如果 <input type="checkbox" id="check" ...>  沒有勾選,會傳回 "undefined"
不是傳回 true false !!

如果撰寫時已加入 checked 屬性 <input type="checkbox" id="check" checked ...> ,則不管有無勾選,用 $("#check").attr("checked")   都會傳回 "checked",根本無判斷是否已勾選!!
要能正確以 true/false 判斷是否勾選應該使用
$("#check").prop("checked")     // prop  才會回傳 true 或 false

要如何利用jQuery勾選checkbox? 下面兩條敘述都可以正確達到目的
$("#check").attr("checked",true);
$("#check").prop("checked",true);

甚至  $("#check").attr("checked","false"); 也會讓 checkbox 被勾選,但
$("#check").attr("checked",false);  則不會被勾選!!  (注意 false 有無用 "  " 括起來!)

你以為 <input type="checkbox" checked="false"> 是否就是取消勾選 ? 底下是我實驗的程式碼,您也可以試試看。
<html>
<head>
    <title></title>
    <script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
    <script type="text/javascript">
        $(document).ready(function() {
            $("#imgbtnSubmit").click(function() {
                alert("chk1.Attr: " + $("#chk1").attr("checked"));
                alert("chk1.Prop: " + $("#chk1").prop("checked"));
                alert("chk2.Attr: " + $("#chk2").attr("checked"));
                alert("chk2.Prop: " + $("#chk2").prop("checked"));
            });

           $("#imgbtnChk").click(function() {
                $("#chk1").attr("checked",true);
                $("#chk2").prop("checked",true);
            });
        });
    </script>
 </head>
<body>
    <form id="form1" style="width:100%;">
    <input type="checkbox" id="chk1" value="0" />
    <input type="checkbox" id="chk2" value="0" checked=false />
    <input type="button" id="imgbtnSubmit" value="確定" >
     <input type="button" id="imgbtnChk" value="勾選" >
    </form>
</body>
</html>

2013年6月23日 星期日

利用javascript實做 MVC 網頁(六)

雖然一直想儘量減少在網頁上嵌用後端的程式碼,但如果網頁不和後端溝通就無法實現Web2.0的互動性,所以就思考是否有一個準則,上圖就是我想到的標準!
  1. 如果Request 的對象是一般靜態網頁(這種網頁根本不需任何 ActivePage支援)、或初始時之欄位內容都是固定的(例如都是以空白開始),因為Response 前不需先進行任何處理,所以就直接回應即可,網頁亦不需內嵌任何後端程式碼,甚至網頁都可以用  .htm/.html 的副檔名。
  2. 如果網頁是接收自其他網頁的要求(Post/Get),而用其他網頁的欄位內容做進一步處理時,因為本網頁本身沒有處理Form的邏輯,所以利用內嵌在頁面的後端函數(ParaFetch)將需求網頁之 Form 欄位組合成 Key:Value 對,此時會將 Key:value  以JavaScript 物件方式,指定給本網頁,所以在本網頁載入完成後,再由 window.onload 事件以AJAX方式向後端要求處理這些 key:value ,後端將處理結果回傳後,再利用 FormRender 來展現頁面。
  3. 第三種不建議使用,因為會破壞使用這種架構的原則,但有時為了方便,而且網頁是最終結果,不需再做其他後續的操作,就直接利用 ASP.NET 在後端 Render 網頁內容(例如報表等)

2013年5月23日 星期四

利用javascript實做 MVC 網頁(五)

離發想到現在竟然不知不覺過了一年了! 這期間一直在想如何精簡實作的方法,好讓這種開發模式可以容易實做。經過前面幾篇的說明,在此先做個小結,草擬設計原則,以利設計參考:
(一)對資料庫的存取、控制寫在 Module內,其他程式(Control, View)不能包含任何存取資料庫的功能--以「物件」方式實作對資料庫的存取,提供函數(method)呼叫供Control使用
(二) Module  只實作業資料庫的存取功能,不作其他額外功能。
(三) Control  :
  1. 負責接收 View Submit過來的資料及回傳處理結果。
  2. 負責所有的商業邏輯的計算(處理)。
  3. 如果要查詢資料庫,只將查詢參數傳遞結 Module 的函數(method),並接收 module 回傳的查詢結果。(Control 不自己做 SQL 的查詢語法 )
(四) View 負責所有前端的Presentation的功能:
  1. View 傳送表單資料或使用者操作給 Control,Control用 JSON 或 Response 回傳處理結果。
  2. View 收到結果後,由View 決定回傳值的操作,Control 只負責傳回結果,不負責控制 View 的表現方式。
(五) 由於View 內容包含:HTML(網頁), CSS(外觀佈局), JavaScript(行為控制),為了實現網頁設計(HTML + CSS) 與行為控制設計(JavaScript) 分開,建議:
  1. 有關網頁的 Javascript 建議以 <script type="text/javascript" src="網頁名.js"></script> 方式自外部引入。
  2. 如果共用的 javascript  檔案,置放於根目錄下的 JS 子目錄,如果是特定網頁專屬的Javascript 檔案,則與網頁置於同目錄下,並採用相同主檔名,以利管理
  3. 有關各DOM物件的事件處理,請利用 jQuery 的 $("#id").EVENT=function(){ }; 方式註冊,不要直接寫在 Html 的 tag 內。
利用這種開發模式,個人見解應有下列優點:
  1. 網頁、Javascript、Control、Module 可以分開設計、測試,也就是可以平行開發。
  2. 各功能間是透過功能呼叫 (參數傳遞)方式合作,對於通用的功能(例如:可能許多網頁都會叫用相同的資料)只需寫一個即可共用! 減化開發程序及版本維護。
  3. 由於各功能獨立模組化, 可供其他專案使用,達到程式再用目標。
  4. 為客戶展示用的雛型網頁可以幾乎不修改就直接移作正式開發用,讓客戶獲得一致的感受
依照上述原則,應該可以達到 DB管理、程式設計與網頁設計分離的要求。
由於個人思考範有限,若先進有發現不足之處,尚請隨時指正!

2013年5月22日 星期三

利用javascript實做 MVC 網頁(四)

前言:

利用javascript實做MVC 網頁(二) 談到如何將 A 網頁的表單資料 POST 給 B 網頁(在 B網頁後端接收後,轉成 javascript 物件,以 <% =變數 %> 方式嵌入網頁中。
利用javascript實做 MVC 網頁(三)中談到如何將後端處理的結果,如何以物件方式返回給網頁內的呼叫程式。

一般由 jQuery.ajax 呼叫後端程式時(詳見:利用jQuery -Ajax 叫用 ASP.NET 的幾種方式),如果要傳送的表單欄位,則在 ajax 中的 data: 屬性設定欲傳送的 Key:value (值對),如:

data: JSON.stringify({field1:value1, field2: value2, field3:value3})

在接收端,如 WebService 或 PageMethod 則利用函數的參數:

METHOD(string field1, string field2, string field3)  來接收表單欄位內容

註:參數是以名稱對應方式,而非順序對應!

如果表單欄位數二、三個,使用這種傳直方式還不會有多大問題,如果表單位有二、三十個,那參數不是要寫到天邊去了!  顯然參數名稱對表單欄位的方式就不太合用! 這時候我們可以改用字典物件(Dictionary<>)方式來接收!
前端利用 form2json 將表單所有欄位轉成 JSON 物件

data:JSON.stringify({args:form2json("#myform")})

接收端改成

METHOD(Dictionary<string, string> args)
{
   var 變數 = args["欄位名稱"];   //就可以取得傳來的值了!
}


form2json 程式碼如下:
function form2json(formId)    //formId 是要加入 JSON 的表單 id 
{
    var data = new Object();
   $(formId +" :input:not(:button, :reset, :submit)").each(function()
    {
        if($(this).attr('type')=='checkbox')
        {
                data[$(this).attr('name')]=($(this).attr('checked')) ? 1 : 0 ;
        }   else     {
            data[$(this).attr('name')]=$(this).val();
        }
    });
    return data;
} 

後記:自己改寫了一支 jQuery Extention,只要在網頁引入:
 <script type="text/javascript" language="javascript" src="js/cmj.toJSON.js"></script>
然後在 data 用
data: JSON.stringify({arg:$('#myform').toJSON()}),
就可以輕鬆將表單欄位組成 JSON 物件了!

使用jQuery.ajax呼叫 WebService或 ASP.NET 在後端程式指定轉址路徑

  當利用 jQuery ajax 呼叫或Post 給後端 ASP.NET處理時,如果要指定轉址的路徑,可以將返回值用  「window.location.replace("路徑");」  或  「window.location.href ="路徑";」,以字串方式回傳給呼叫的 ajax。
在 ajax 的 success 回呼程序中再利用
   evel(回傳值)
方式來轉址即可!

其實還有很多種方式可以處理,但遺憾的是無法直接在後端程式碼使用:
Response.Redirect("路徑");  的方式來轉址,只能利用前端網頁轉址!
註:用  Server.Transfer("路徑"); 也不行

2013年5月21日 星期二

利用jQuery -Ajax 叫用 ASP.NET 的幾種方式

  jQuery 真是個佛心的發明,它讓前端叫用後端服務的方式更加靈活,使用 jQuery 之 ajax 呼中不同標的之差異:
呼叫  WebService :可用 JSON 或 Key:Value 傳送Form的欄位。
      如果用 return 回傳值,則dataType 要設成 "json"
      如果用 HttpContext.Current.Response.Write 回傳值,則 dataType 設成 "html" 或 "text"
呼叫 PageMethod:只能用 JSON 傳送 Form 欄位。
     只可用 return 回傳值,且dataType 要設成 "json",否則會收到字串形態的 JSON 物件
呼叫網頁(ASPX):只能用 key:value 傳送 Form 的欄位。
     只可用 HttpContext.Current.Response.Write 回傳值,則 dataType 設成 "html" 或 "text"

壹、配合 JSON 呼叫 WebService (ASMX)

處理結束之後,會用 return 返回處理結果,返的資料

$.ajax({
        url: 'user.asmx/checkLogin',
        type: 'POST',
        async: false,
        data: JSON.stringify({userId:'12345', password:'12345'}), 
        contentType:'application/json; charset=UTF-8',
        dataType:"json",      //如果要回傳值,請設成 json
        error: function(xhr) {//發生錯誤時之處理程序 },
        success: function(reqObj) {//成功完成時之處理程序 }
     }); 

底下是 user.asmx 的後端程式碼
using System;
using System.Web;
using System.Web.Services;
using System.Web.Security;
using System.Web.Services.Protocols;
using System.Collections.Generic;
using System.Web.Configuration;
using System.Text.RegularExpressions;

[WebService(Namespace = "http://SSO_Portal.moj.tw/")]
[WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
// 若要允許使用 ASP.NET AJAX 從指令碼呼叫此 Web 服務,請取消註解下一行。
[System.Web.Script.Services.ScriptService]
public class SSO_Service  : System.Web.Services.WebService 
{
    [WebMethod(EnableSession = true)]
    [System.Web.Script.Services.ScriptMethod]
    public string[] checkLogin(string userID, string password, string grCode) 
    {
        UserData user=new UserData();

        HttpContext.Current.Response.Redirect("Default.aspx");
        return null;
     }
}


貳、配合 JSON 呼叫 PageMethod

 處理結束之後,會用 return 返回處理結果,返的資料

$.ajax({
        url: 'checkLogin.aspx/chkUser',
        type: 'POST',
        async: false,
        data: JSON.stringify({userId:'12345', password:'12345'}), 
        contentType:'application/json; charset=UTF-8',
        dataType:"json",       //如果要回傳值,請設成 json
        error: function(xhr) {//發生錯誤時之處理程序 },
        success: function(reqObj) {//成功完成時之處理程序 }
     }); 

底下是 checkLogin.aspx 的 chkuser 後端程式碼
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.Security;

namespace mySpace
{
    public partial class checkLogin : System.Web.UI.Page
    {
        protected void Page_Load(object sender, EventArgs e)
        {
        }
 
        [WebMethod(EnableSession = true)]
        public static string chkUser(string userID, string grCode, string password)
        {
            string aaaa = "";
//          string uid=HttpContext.Current.Request.Form["userID"];  //收不到值
//          string pwd = HttpContext.Current.Request.Form["password"];
//          HttpContext.Current.Response.Redirect("Default.aspx"); //會出錯
            return null;
        }
     }
}


參、直接 POST 給另一個網頁(ASPX)

直接在處理的網頁內,用 Response 返回資料內容

$.ajax({
        url: 'checkLogin.aspx',
        type: 'POST',
        async: false,
        data: {userId:'12345', password:'12345'}, 
        contentType:'application/x-www-form-urlencoded; charset=UTF-8',
            //如果使用 "application/json; charset=utf-8" ,則後端程式無法
            //用 Request.Form["..."]  來取得傳送過來的資料
        dataType:"html",     //或用 "text"
            //如果不指定 dataType,會造成 jQuery 剖析時錯誤,所以會觸發 error
        error: function(xhr) {//發生錯誤時之處理程序 },
        success: function(reqObj) {//成功完成時之處理程序 }
     }); 

底下是 checkLogin.aspx 的後端程式碼
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.Security;

namespace mySpace
{
    public partial class checkLogin : System.Web.UI.Page
    {
        protected void Page_Load(object sender, EventArgs e)
        {
           String userid = this.Request.Form["userId"];
           String pwd = this.Request.Form["password"];
           this.Response.Write("Return Value");  //要返回的文字
           this.Response.Redirect("Default.aspx"); 
           //會把整個 Default.aspx 的html內容回傳,而不是執行轉向作業!
           Server.Transfer("default.aspx"); 
           //會把整個 Default.aspx 的html內容回傳,一樣不會執行轉向作業!
        }
     }
}


說明:

jQuery 的 contentType 對不同後端程式的影響

 application/x-www-form-urlencoded application/json
對WebService
  1. data: 不能序列化,否則會出錯
  2. 可由參數接收(用名字對應),接收參數不一定要等於傳送之個數
  3. 可由 HttpContext.Current.Request.Form 請值
  4. 可用HttpContext.Current.Response. Redirect("../Default.aspx"); 傳送網頁
  1. 只能透過參數接收傳過來的資料
  2. data: 一定要序列化,否則會出錯
  3. 不能使用 HttpContext.Current 物件(編譯時不會錯,執行時會錯!)
對ASPX網頁
  1. data: 不能序列化 JSON.stringify()
  2. 可由 this.Request.Form 請值
  3. 可用this.Response. Redirect("../Default.aspx"); 傳送網頁
  1. 可以呼叫網頁程式,但無法接收 Form 欄位資料
對 PageMethod不能使用這種方式呼叫 PageMethod,否則會變成是 Post 到 ASPX,反而執行 Page_Load,而忽略指定的 PageMethod


  1. 只能透過參數接收傳過來的資料
  2. data: 一定要序列化,否則會出錯
  3. 可以用 HttpContext.Current.Request.Form 但收不到值!
    4.不能使用 HttpContext.Current 物件(編譯時不會錯,執行時會錯!)
序列化:用JSON.stringify({})



jQuery.ajax() 的一般重要參數設定

accepts:??不清楚要設成什麼值??
async:true 是否採 非同步 呼叫,跨網域呼叫 及 dataType:"jsonp" 不支援 同步呼叫
beforeSend:function(jqXHR, settings){}  執行ajax之前先執行此callback程序,例如更改檔頭、 如果返回 false ,就會中止 ajax
cache:true | false
complete:function(jqXHR, textStatus){} 在 success 或 error 之後執行
  txtStatus 可能為 "success", "notmodified", "error", "timeout", "abort", or "parsererror"
contents: 指明針對後端回傳的資料, jQuery 要如何剖析? ????
contentType
    "application/x-www-form-urlencoded; charset=UTF-8"   用一般文字對傳送參數
    "application/json; charset=utf-8"                    用 json 物件傳送參數
converts: ????dataType-to-dataType converters
  {"* text": window.String, "text html": true, "text json": jQuery.parseJSON, "text xml": jQuery.parseXML}
crossDomain: false 是否允許跨網域 呼叫
data: {key:value, key:value} 要傳送的資料
dataFilter: function(String data, String dataType) 如何處理由後端回傳的原始資料
dataType:"xml", "json", "script", "html", "jsonp" 指明回傳資料的形態,預設是由 jQuery 自行判斷
scriptChartset: 只針對 dataType 設成 script 或 jsonp 是指定其內碼字集
error:function(jqXHR jqXHR, String textStatus, String errorThrown )
headers:{key:value, key:value} request 時,加入額外的表頭資訊
processData:true 預設 data: 選項的資料是會依照 contentType 進行轉換,如果不想轉換可將 processData 設為 false
success:function(PlainObject data, String textStatus, jqXHR jqXHR )
timeout:N 設定 timeout 的時間(毫秒)
type: "GET" 或 "POST" ("PUT", "DELETE" 也可使用但不一定有作用)

2012年5月10日 星期四

jQuery JSON 與 ASP.NET WebService 有關日期返回值處理經驗談

設計 Ajax 習慣使用 jQuery 的 ajaxr功能,搭配 .NET 的 WebService 來提供資料,一般資料的傳遞並無問題,但由 WebService返回 DateTime 物件時, Client 端的 JSON(使用http://www.json.org/ json2.js )無法完全轉成日期物件,而是字串,使得物件的序列/反序列不完全。

經追蹤測試,由WebService 回傳 (ObjectQuery<UserData> 一個Entity Framework 的資料表物件),在 return 時是否使 Serialize()進行序列化,會有不同結果
ID:      帳號 (String)
PWD:密碼(String)
Unit:   所屬單位(String)
Authorize:層級(String)
Name:  姓名((String)
ModifiedDate:資料更動日期(DateTime)
CreatedDate: 資料建立日期(DateTime)
FailedLoginCount:登入失敗次數紀錄(int)
LastLoginDate:最近一次成功登入日期 (DateTime)
LastFailedLoginDate:最近一次登入失敗日期(DateTime)


若在 ASP.NET 先經 JavaScriptSerializer.Serialize((ObjectQuery<UserData>)users) 進行序列化,則回傳 jQuery 字串為:
"{"d":"[{\"ID\":\"a\",\"PWD\":\"7367cc4cee061a476290d18978830414\",\"Unit\":\"資訊室\",\"Authorize\":\"1\",\"Name\":\"aA\",\"ModifiedDate\":\"\\/Date(1217994242220)\\/\",\"CreatedDate\":\"\\/Date(1217992404960)\\/\",\"FailedLoginCount\":null,\"LastLoginDate\":null,\"LastFailedLoginDate\":null,\"EntityState\":2,\"EntityKey\":{\"EntitySetName\":\"UserData\",\"EntityContainerName\":\"DraftEntities\",\"EntityKeyValues\":[{\"Key\":\"ID\",\"Value\":\"a\"}],\"IsTemporary\":false}}]"}"


jquery於ajax完成後,如果成功,則 success:function(reqObj) 會自動調用 JSON.parse(reqObj)對回傳資料進行剖析後(注意:.Net 的真正資料是存在 reqObj.d裡),所以在變成下列「字串」,必須再將 JSON.parse()處理一次。
reqObj.d="[{"ID":"a","PWD":"7367cc4cee061a476290d18978830414","Unit":"資訊室","Authorize":"1","Name":"aA","ModifiedDate":"\/Date(1217994242220)\/","CreatedDate":"\/Date(1217992404960)\/","FailedLoginCount":null,"LastLoginDate":null,"LastFailedLoginDate":null,"EntityState":2,"EntityKey":{"EntitySetName":"UserData","EntityContainerName":"DraftEntities","EntityKeyValues":[{"Key":"ID","Value":"a"}],"IsTemporary":false}}]"


但因為就算重新 parse,日期部分仍為字串 「/Date(1217994242220)/」, Client端必須另行對日期欄進行處理  eval(result[i].LastLoginDate.replace('/','"')) 才能真正得到 javascript 的日期物件。

asp.net允許物件不經過JavaScriptSerializer.Serialize(物件) 序列化,而直接回傳 (ObjectQuery<UserData>)users 物件的字串,
"{"d":[{"__type":"WebTemplate.Shared.UserData","ID":"a","PWD":"7367cc4cee061a476290d18978830414","Unit":"資訊室","Authorize":"2","Name":"aA","ModifiedDate":"\/Date(1217994242220)\/","CreatedDate":"\/Date(1217992404960)\/","FailedLoginCount":null,"LastLoginDate":null,"LastFailedLoginDate":null,"EntityState":2,"EntityKey":{"EntitySetName":"UserData","EntityContainerName":"DraftEntities","EntityKeyValues":[{"Key":"ID","Value":"a"}],"IsTemporary":false}}]}"
經 JSON.parse(jquery會自動調用)後變成「物件陣列」,唯日期欄位內容會以文字方式表示:
"/Date(1217992404960)/"

如果再將返回之物件傳給 JSON.parse() 剖析,會產生錯誤(例外事件)

由上面的結果可知,不論 .Net 端是否經過序列化(Serialize)過程,經由 jQuery ajax 取得回傳的物件,其中日期物件部分,皆以"\Date(nnnnn)\" 之字串型式回傳
為了解決這個問題,最徹底的方法就是改寫 json2.js,修正說明如下:(我取得的版本是 2011-10-19)

、原程式的 462-465 行,是用來判斷字串是不是可以轉換成 javascript 物件:
if (/^[\],:{}\s]*$/
         .test(text.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g, '@')
                   .replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, ']')
                   .replace(/(?:^|:|,)(?:\s*\[)+/g, ''))) {
 
而 472 行就是利用 eval 將字串轉成 物件。
                j = eval('(' + text + ')');


、所以我們要修改這裡的程式,使其可將  /Date(nnnnn) / 轉成  new Date(nnnn)
首先,原始由 .Net 回傳的日期在字串的為
     \"\\/Date(nnnnn)\\/\"   (經過 序列化)
            或
    "\/Date(nnnnn)\/"     (直接物件回傳)

所以我們在丟給 eval 之前要先將上面的字串內容換成   new Date(nnnnn),於是我在 461 行,加入了:
text = text.replace(/(\\\"\\\\\/){1}(Date\(\d*\)){1}(\\\\\/\\"\\){1}/g, "new $2")
                       .replace(/(\"\\\/){1}(Date\(\d*\)){1}(\\\/\"){1}/g, "new $2"); 

 但加了這一行之後,只要有日期物件傳入,就會引發例外,原來 new Date(nnnnn) 無法通過上述 462 - 465 的 test ,因此還還修改462 - 465 的程式碼,如下列藍字部分
if (/^[\],:{}\s]*$/
                    .test(text.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g, '@')
                        .replace(/new Date\([0-9]*\)/ig, '')
                        .replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, ']')
                        .replace(/(?:^|:|,)(?:\s*\[)+/g, ''))) {


這樣就可以應付上列兩種日期字串格式。

三、 記得:在 .Net 端如果有進行 JavaScriptSerializer.Serialize()序列化,在Client端除了 jQuery自動調用 JSON.parse() 外,還必須在程式中進行一次 JSON.parse(retObj.d),才會得到真正的物件內容。如果.Net 沒有進行 JavaScriptSerializer.Serialize()序列化,就不須再一次 JSON.parse(retObj.d),不然會出錯!

** 此方法僅對 .Net 之回傳資料測試通過,其他程式語言未測試 ,不保證適用 **

XXX 為什麼 IE6 可以自動調用 parse,但在 IE8、FireFox  就是不行 ?????
原來 IE8 與 FireFox(11)已內建原生 JSON  程式,所以 jQuery 會直接調用原生的 JSON.parse,以致上面所述修改 json2.js 的功用皆失效,根本不會叫用 json2.js。
查看 json2.js  程式碼之 162 至 165 來

var JSON;
if (!JSON) {
    JSON = {};
}

即判斷如果已註冊 JSON 物件,則 json2 就不再註冊此功能,為了能使用上面針對日期物件修正的功能,只好把 163、165兩行註解掉,強制 jQuery 使用 jsons2.js 的 JSON 功能!

var JSON;
// if (!JSON) {
    JSON = {};
//}

相關文章:JSON.parse 無去剖析 .NET2 的JSON 日期
修改後之下載(zip檔)  json2.cmj.js

2012年5月1日 星期二

利用javascript實做 MVC 網頁(三)

前面利用javascript實做 MVC 網頁(二) 已說明如何將Submit 的 Form 傳給後端的程式,也說明後端程式如可將對應的欄位資料及屬性以 Dictionary<> 方式傳給前端的 AJAX (或插入 javascript 的變數),接著就來探討前端在接收到 Dictionary 物件後如何對應指定給網頁上的欄位(以 id 來對應)!
這篇續文拖了一個多月,因為在此期間另外被要求開發Android上的App (題庫模擬測驗,改天再發表成果),所以暫停了這個主題的實作。
不論從網頁Submit過來的資料,或由後端回傳的資料,我們都已用JSON格式轉化成字串,故此前端的Javascript必須解析此一字串的內容,雖然說寫程式要站在巨人的肩膀上,可是現成處理前端往後送的模組很多,例如:jForm,甚至jQuery內帶的功能都可以幫我們把欄位或表單打包來Post給後端程式,但是處理JSON 資料對表單欄位對應的模卻少見,最多的是表格(Grid)的處理,所以就自己動手寫一支! cmj.formRender.js 可以從我的 google.Docs 下載!
對照利用javascript實做 MVC 網頁(二)的說明,要供前端應用的JSON字串會長成這個樣子:
{"userid":{"value":"i123","style":"color:red;text-align:right;"}} (單一欄位時) 或

{"userid":{"value":"i123","style":"color:red;text-align:right;"},
"title":{"value":"科長","style":"color:red;text-align:right;"}} (多欄位時) 或

[{"value":"1","style":"color:red;text-align:right;","text":"選項一"},
{"value":"2","style":"color:blue;text-align:left;","text":"選項二","selected":"selected"}] (單欄多值時)
上列的字串要先經 JSON.parse() 轉換成 JavaScript 的物件,再傳給 cmj.formRender.js 的函數!

cmj.formRender.js 提供5個函數:
rendForm(formData) :  會利用欄位的 id 屬性,自動設定欄位的值(value 或  text 或 checked 或 selected)

而下方的四個函數則針對 Option, Radio, CheckBox 及 List 的操值,這些欄位有一個共同的特性:多個選項清單

rendOption(parentObj, targetData, addTo)
rendRadio(parentObj, targetData, addTo, itemsPerLine)
rendCheckbox(parentObj, targetData, addTo, itemsPerLine)
rendList(parentObj, targetData, listType, itemsPerLine, addTo)
    說明:
       parentObj $(SELECTOR) 找到的物件
        targetData: 即上面回傳經 JSON.parse() 後的物件
        addTo: 是否附加到現有的項目之下(或者要先清現有項目)
        itemsPerLine: 針對 checkbox, radio 橫向要顯示幾個項目。
        listType: 字串 checkbox, radio, ol, ul 表示要產製的項目類別,rendRadio/rendCheckbox 會呼叫此一函數
也就是說呼叫rendCheckbox(parentObj, targetData, addTo, itemsPerLine)
和呼叫
       rendList(parentObj, targetData, 'checkbox', itemsPerLine, addTo)
是一樣的!

透過上面的5個函數,幾乎可以處理頁面上欄位「值」的問題,至於其他特效或 Grid 就借用現成的模組了!

系列連結:
利用javascript實做 MVC網頁(一)
利用javascript實做 MVC網頁(二)

2012年4月30日 星期一

JSON.parse 無去剖析 .NET2 的JSON 日期

(http://www.json.org/ 提供各種程式語言上使用的 JSON元件)
當選用http://www.json.org/ 提供的 json2.js ,搭配 ASP.NET 進行資料溝通時,會發現由 ASP.NET回傳的物件如果包含有日期物件時,json2.js 的 JSON.parse() 解析功能會出錯。
經追蹤結果:
日期物件在 .NET 端利用 JSON.toJSON(物件) 進行序列化時,會被轉成 new Date(毫秒值) 的字串,當此字串傳給前端 json2.js 的 JSON.parse(字串)時,在462 到 465行的地方執行之後,會引發錯誤
            if (/^[\],:{}\s]*$/
                    .test(text.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g, '@') 
                        .replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, ']')
                        .replace(/(?:^|:|,)(?:\s*\[)+/g, ''))) {
只要再463行之後再加入 .replace(/new Date\([0-9]*\)/ig,'') 即可。
因為原來的 RegExp取代並不會算到 new Date(),所以 /^[\],:{}\s]*$/.test 結果永遠為 false,加入
.replace(/new Date\([0-9]*\)/ig,'') 之後
 
            if (/^[\],:{}\s]*$/
                    .test(text.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g, '@')
                        .replace(/new Date\([0-9]*\)/ig,'')
                        .replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, ']')
                        .replace(/(?:^|:|,)(?:\s*\[)+/g, ''))) {
 
就可以讓 new Date(...) 也可通知 test 
 

利用javascript實做 MVC 網頁(二)

開發ASP.NET網頁總覺得卡卡地,故思能否將網頁與後端程式分離,原由見 利用javascript實做 MVC 網頁(一)
經幾日來的摸索,似乎無法達到完全分離的目標,畢竟由一個網頁submit表單到另一個網頁時,仍需依靠後端程式來接收及預做處理,接收的工作無法由前端的 Javascript 來達成,整個實作原則說明如下:(還望有高手指點,提供更佳做法)

一、如網頁不需接收表單資料(來自它頁或自頁),則此網頁可以單純以 Html+Javascript 撰寫,例如:Login頁

二、如果網頁需接收表單資料:
(方式一)利用 或 先引入(#include file="file.aspx" , <% =function()%>)接收表單資料的程式。
virtual:是相對於url的路徑;file:是相對於檔案系統的路徑。

(方式二)利用CodeBehind 將接收表單資料的程式寫在 Page_Init 區段或Page_Load區段。

不論使用方式一或方式二,都可以把表單結果以Javascript物件或陣列方式,插入網頁中。


<!--#include file="pageload_fun.aspx"--> 

<script language="javascript" type="text/javascript"> 

var formObj=<%=formData%> 

</script>
(20130525補充將Post的資料直接輸往前端,有時可能不妥,此時可利用 Session 先紀錄著,而傳送到前端的是 Session的 Index,這個 Index 可是使用 GUID)

則前端網頁之JavaScript就可以操作 formObj,利用此技巧,任何後端要交付前端處理的資料皆可輕鬆達成!


pageload_fun.aspx 內容:

<%@ Import Namespace="System.Web.Script.Serialization" %>
<%@ Import Namespace="System.Collections.Generic" %>
<script language="C#" runat="Server">
    Dictionary<String, String[]> formItems = new Dictionary<string, String[]>(); //做為 json 轉換前的接收物件
    string retStr = "", jsonStr = "";

    void Page_Load(object sender, System.EventArgs e)
        {
            JavaScriptSerializer js=new JavaScriptSerializer();
          
            NameValueCollection postForm;
            postForm = Request.Form;
            String[] arrKeys = postForm.AllKeys;
            for (int i = 0; i < arrKeys.Length; i++)
            {
                retStr += arrKeys[i] + ":" + postForm.GetValues(arrKeys[i])[0];
                if ((i+1) < arrKeys.Length) retStr += "&";
                //上面是以字串拼接方式回傳,下面是用 Dictionary 轉 json 方式回傳
                formItems.Add(arrKeys[i],postForm.GetValues(arrKeys[i]));
            }
            if (null != Session["_ValCheckCode"]) retStr += "
Session:" + Session["_ValCheckCode"].ToString(); //只是為測試驗證碼是否有改變而已
            jsonStr =  js.Serialize(formItems);
            Response.Write(retStr + "
" + jsonStr);
        }
</script>

  如果希望後端傳回前端有關網頁欄位之產製資料(例如:要對 id 欄位設定值及style、size 等),則資料須以 Dictionary 傳回,而傳回的 Dictionary 型態為:

Dictionary<string, Dictionary<string, string>>

外層的 key 對應到網頁欄位的 id,而內層的 Dictionary 則是指定的 Attribute 及其值,參考代碼:
    Dictionary<string, string> Attr=new Dictionary<string,string>();
    Dictionary<string, Dictionary<string,string>> D=new Dictionary<string,Dictionary<string,string>>();
    Attr.Add("value", "i80091");
    Attr.Add("style", "color:red");
    D.Add("userid", Attr);
    Dictionary<string, string> Attr2 = new Dictionary<string, string>();
    Attr.Add("value", "cmj521204");
    Attr.Add("style", "color:blue");
    D.Add("pwd", Attr);           
           
    retVal = JSON.toJSON(D);

系列連結:
利用javascript實做 MVC網頁(一)
利用javascript實做 MVC網頁(三)

2012年4月23日 星期一

利用javascript實做 MVC 網頁(一)

網頁開發技術、開發模式不計其數,我無法窮究所有而知之,底下的構想也許已經有人在做了,只是我 孤陋寡聞,未聞其詳,如果有雷同,絕非抄襲。若有先進知其所以,亦請不吝指教! 由於一切尚在發想之中,並未完全實作出可用的系統,有關做法將分數篇說明。


源起
以前用 ASP 開發網站程式,都是在網頁中包程式碼,「專家說」這樣的寫法不好,網頁和程式混雜,不易系統維護,所以微軟推出 ASP.NET後,雖然繼續支援 Inline 的寫法,但專家都說這樣不好,所以開始改用 Code Behind寫法, 但總覺得 Code Behind把畫面和程式綁得很緊,造成美工人員設計好的頁面碼,程式人員還得再調整一次。

再者,VisualStudio的頁面設計不是那麼好用,甚至微軟提供的元件,在設計上有些繁雜,如果沒有 ASP.NET的背景,根本無法使用!
更重要一點,利用 ASP.NET設計的頁面,如果要轉換成別的語言,幾乎連頁面都需要整個重新改寫,降低了系統的再利用率,而後端程式和網頁過度相依,也影響程式的測試(尤其是單元測試)。

因為一直沒辦法好好駕馭CodeBehind(自認資質不好),每當遇到要在前端使用JavaScript時更是痛苦,一直苦思有沒有好一點的方式,可以把網頁和程式碼分開。或許你會建議我使用MVC,是的,我也試過用 ASP.NET MVC ,可是仍不能擺脫網頁再利用率低的問題!


目標
  1. 網頁可以獨立設計,可以真正使用前端元件(如jQuery.UI)
  2. 前端程式碼,可以直接寫在網頁裡,不需要利用後端程式碼來安插(前端歸前端,後端歸後端)
  3. 轉換平台時,可以保留前端的網頁設計(至少可以變動最小)
  4. 就算沒有網頁,也可以對後端程式進行測試驗證(讓網頁與程式可以獨立開發)
  5. 美工人員設計好的網頁,可以直接拿來做雛型展示,客戶審視合格的網頁雛型即可提供程式人員著手開發。 

理想架構
發想
網頁系統主要是由 UI(View)、後端程式(Control)、資料管理(Module)及資料(DB)所組成,為了能分模塊開發,所以有 MVC 的開發模式,我的構想也是借用MVC的概念:

  1. 網頁屬UI,應該只用來呈現資訊、收集使用者的要求及回應要求
  2. 後端程式負責餵資料給前端網頁,至於畫面的呈現方式,及各欄位填值,都由前端的Javascript負責,即原本由後端指定欄位內容的做法,改由前端處理。
  3. 如果前端可以處理的,就不要丟回後端。
  4. 如果不需要換網頁,就用AJAX向後端提出要求!
  5. 後端程式(Control)獨立於網頁,而且只運行必要的運算,如身分檢核、資料內容驗證。
  6. 需要跟資料庫溝通者,透過模組(Module)運作,模組負責向資料庫讀資料並回復給要求者,及應要求將資料寫入資料庫。
  7. 如果網頁需要資料,必須透過後端程式向模組提出要求,模組不直接公開使用。
問題
在實驗的過程中遇到幾個未能克服的問題:
  1. HTML本身無法處理Request,從瀏覽器POST回去的表單內容無法於直接於HTML中動態使用,仍需藉用後端的平台協助處理。
    • 目前解法是在網頁的最前頭利用 ASP.NET接收,再轉成javascript變數嵌於網頁上,雖然無法完全達成上面的目標,至少此一接收的程式很簡單,而且可以獨立成一個檔案(整個系統都用同一支),只要在網頁上用一行 <% =getPostData %> 就可以達成,對美工人員不會造成困擾。
    • 對於日後更換平台,只需將網頁之副檔名更名,再將後端程式換成對應的平台的程式碼即可!
  2. 如果使用 WebService來處理前端的需求,WebService無法直接處理 Redirect 的要求,所以後端程式(Control)不能全部用WebService來實作(原本是想全部用WebService實作後端)
    • 所以若有Redirect需求者,就必須POST給 .aspx 的網頁程式。
  3. 無法享用 ASP.NET MasterPage的好處,MasterPage一直是我製作網頁時愛用的方式,它可以讓我把網站風格一次搞定,可惜這是ASP.NET的特異功能,一般網頁是無福受用。
    • 還好尚有frame可利用,雖然沒有MasterPage完美,但至少解決了網站風格一致性的問題。
系列連結:

利用javascript實做 MVC 網頁(二)
利用javascript實做 MVC網頁(三)

2013.05.17 修正用字

2011年1月31日 星期一

解決jQuery Editable 回上一頁時,選單資料錯亂!

jed(jquery-jec Editable combobox for jQuery )的預設參數如下表,當 使用預設值時,如果使用者用瀏覽器的「上一頁」返回時,則原來選單中的資料都會被移一筆,而造成資料錯誤,只要在設定
$(function () {
$('#cbonam_pros').jec({'position':999});
});

強迫position預設值為999,就會將手動輸入設定在最後一筆,就不會影響原來選單的選項結果了!
Type Name Description Default value Added in
int position index of editable option to be inserted in select 0 1.2
bool ignoreOptGroups ignores option groups when placing editable option if set to true false 1.3
int maxLength max input length 255 1.3
string array classes additional classes to be added [] 1.2
object styles additional CSS properties to be set {} 1.2
string array optionClasses additional classes to be added to editable option element [] 1.2
object optionStyles additional CSS properties to be set for editable option element {} 1.2
bool triggerChangeEvent triggers change event on select everytime user types if set to true false 1.3
bool focusOnNewOption moves focus to newly created option element if set to true false 1.2
bool useExistingOptions uses selected <option> text as a base for editable option if set to true, otherwise editable option base is empty string false 1.2
array ignoredKeys ignored key codes, values in array can be either a {min: MIN_VALUE, max: MAX_VALUE} , {exact: VALUE} value or integers [] 1.2
array acceptedKeys accepted key codes, values in array can be either a {min: MIN_VALUE, max: MAX_VALUE} , {exact: VALUE} value or integers [{min:32, max:126}, {min:191, max:382}] 1.2

2010年12月25日 星期六

解決jquery Editable Combobox (jec) 無法輸入中文修改方式

jec.js 可以讓 select - option  增加使用者自行輸入功能,並且將輸入的資料加到List中,但是從 http://stuff.rajchel.pl/jec/ 下載下來的 jquery.jec.js 並無法輸入中文,必須自行修改 jquery.jec.js 內容,找到檔案中(就在檔案前面而已):

defaults = {
position: 0,
ignoreOptGroups: false,
maxLength: 255,
classes: [],
styles: {},
optionClasses: [],
optionStyles: {},
triggerChangeEvent: false,
focusOnNewOption: false,
useExistingOptions: false,
blinkingCursor: false,
blinkingCursorInterval: 1000,
ignoredKeys: [],

acceptedKeys: [[32, 65535]] //126], [191, 382]]

};

原來 acceptedKeys: [[32, 126], [191, 382]] 所以只接受英文字母,但中文字的keyCode為兩Bytes,最大可到65535,只要調整成 acceptedKeys: [[32, 65535]] 就可以輸入中文字。

要使用 jec,只要引入 jQuery.j, jQuery.jec.js,並且在<head></head>中加入

$(function () { $('#cbonam_doc').jec(); });

即可讓 select 變成可手動輸入!