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

2012年9月19日 星期三

Returning a value from a PageMethod

NOTE: This is designed for v1.0.61025.0 of AJAXExtensionsToolbox.dll
If you've ever returned data from a web service using MS AJAX this technique will look familar.
Call the method from javascript by append PageMethods. to the front of the method name. Add a delegate to the end of the method's parameters to get the result.
<script language="javascript" type="text/javascript">
    function MyMethod_Result(ResultString)
    {  
        alert(ResultString);
    }
    
    function CallMyMethod(){
        PageMethods.MyMethod("World", MyMethod_Result);
    }
</script>
I have made the source to this available to download, otherwise take a look at the full explanation below:

Full explanation - Step 1 - Enable Page Methods on your ScriptManager

Set the EnablePageMethods attribute to true
<asp:ScriptManager ID="ScriptManager1" 
    EnablePageMethods="true" 
    EnablePartialRendering="true" runat="server" />

Step 2 - Mark your method as static and give it the WebMethod attribute

The method has to be declared static. It must also be marked with the WebMethod attribute. You'll probably find that you need to include System.Web.Services
using System.Web.Services;
[WebMethod]
public static string MyMethod(string name)
{
    return "Hello " + name;
}

Step 3 - Call it from Javascript

Call the method from javascript by append PageMethods. to the front of the method name. Add a delegate to the end of the method's parameters to get the result.
<script language="javascript" type="text/javascript">
    function MyMethod_Result(ResultString)
    {  
        alert(ResultString);
    }
    
    function CallMyMethod(){
        PageMethods.MyMethod("World", MyMethod_Result);
    }
</script>

2011年10月3日 星期一

如何解决:window.opener=null;window.close(),只支持IE6不支持IE7的问题?

很简单,请使用这段代码:
<a href="#" onclick="window.opener=null;window.open('','_self');window.close();">不确认关闭</a>
也就是在window.opener=null;window.close()之间加入window.open('','_self');就OK了!

程式碼工作室
ERP/EIP/CMS/CHART/REPORT/SPC/EDA/雲端 系統開發整合
ASP/PHP/JSP/ASP.NET 網頁設計
技術指導顧問
信箱:paulwu0114@gmail.com
http://www.coding.com.tw

2011年10月2日 星期日

JAVASCIRPT 切換圖片

<script language=javascript>
var elady_step=3; //1:small, 3:middle, 5:big
var elady_speed=50; //20:fast, 50:middle, 80:slow
var e_tp=new Array();
var e_tplink=new Array();
var adNum_elady1=0;
var elady_stop_sh=0;
var elady_star_sh=1;
function elady1_moveImg(){
if ((!document.all&&!document.getElementById)||(elady_stop_sh==0)) return;
if (elady_star_sh==1){
document.all.elady1_divimg.style.pixelTop=parseInt(document.all.elady1_divimg.style.pixelTop)+elady_step;}
else if (elady_star_sh==2){
document.all.elady1_divimg.style.pixelLeft=parseInt(document.all.elady1_divimg.style.pixelLeft)+elady_step;}
else if (elady_star_sh==3){
document.all.elady1_divimg.style.pixelTop=parseInt(document.all.elady1_divimg.style.pixelTop)-elady_step;}
else{
document.all.elady1_divimg.style.pixelLeft=parseInt(document.all.elady1_divimg.style.pixelLeft)-elady_step;}
if (elady_star_sh<4) elady_star_sh++;
else elady_star_sh=1;
setTimeout("elady1_moveImg()",elady_speed);}
//切换的图片路径
e_tplink[0]="";
e_tp[0]="http://www.coding.com.tw/eip.png";
e_tplink[1]="";
e_tp[1]="http://www.coding.com.tw/erp.png";
e_tplink[2]="";
e_tp[2]="http://www.coding.com.tw/tsmc.png";
e_tplink[3]="";
e_tp[3]="http://www.coding.com.tw/cms.png";
e_tplink[4]="";
e_tp[4]="http://www.coding.com.tw/god.jpg";
e_tplink[5]="";
e_tp[5]="http://www.coding.com.tw/std.jpg";

e_tplink[6]="";
e_tp[6]="http://www.coding.com.tw/house.jpg";
e_tplink[7]="";
e_tp[7]="http://www.coding.com.tw/rent.jpg";
var currentimage=new Array();
for (i=0;i<=3;i++){currentimage[i]=new Image();
currentimage[i].src=e_tp[i];
}
function elady1_set(){ if (document.all)
{ e_tprotator.filters.revealTrans.Transition=Math.floor(Math.random()*23);
e_tprotator.filters.revealTrans.apply(); }
}
function elady1_playCo()
{ if (document.all) e_tprotator.filters.revealTrans.play()
}function elady1_nextAd(){ if(adNum_elady1<e_tp.length-1)adNum_elady1++ ;
else adNum_elady1=0;
elady1_set();
document.images.e_tprotator.src=e_tp[adNum_elady1];
elady1_playCo();
theTimer=setTimeout("elady1_nextAd()", 4000);}
function elady1_linkurl(){ jumpUrl=e_tplink[adNum_elady1];
jumpTarget='_blank';
if (jumpUrl != ''){ if (jumpTarget != '')window.open(jumpUrl,jumpTarget);
else location.href=jumpUrl;
}}
function elady1_listMsg()
{ status=e_tplink[adNum_elady1];
document.returnValue = true;}
window.onload=elady1_nextAd;
document.write("<div id='elady1_divimg' style='position:relative'>");
document.write('<a onMouseOver="elady1_listMsg();return document.returnValue" href="javascript:elady1_linkurl()" target="_self">');
//图片宽高的设定
document.write('<img style="FILTER: revealTrans(duration=2,transition=20)" height=217 src="" width=361 border=0 name=e_tprotator ></a>');
document.write("</div>");
</script>

程式碼工作室
ERP/EIP/CMS/CHART/REPORT/SPC/EDA/雲端 系統開發整合
ASP/PHP/JSP/ASP.NET 網頁設計
技術指導顧問
信箱:paulwu0114@gmail.com
http://www.coding.com.tw

2011年7月7日 星期四

先透過PageMethods撈DB,來決定按鈕是否要confirm訊息

前言

會寫這篇文章的起因來自於MSDN forum上的一個問題:用confirm中斷再繼續執行,updatepanel就不會更新

以前也有遇過這樣的需求,在針對敏感或重要的資料進行操作時,如果那些資料符合了某些條件,
則需要在畫面上顯示提示訊息,也就是confirm,讓User可以知道這筆資料的狀態,再來決定是否繼續執行動作。

這問題的麻煩之處,在於client端與server端事件順序,在這需求裡面會有點卡住。
confirm是javascript的語法,也就是要在頁面Render出來的時候,就該註冊上去的code。
但是我們的confirm,需要根據user在畫面輸入的資料,才能判斷是否需要顯示confirm訊息。
也就是「不能提早註冊confirm的script在button上」。
流程應該是,
  1. 點了按鈕後,去DB撈資料判斷是否要提示user,撈資料的部分code應該寫在server端。
  2. 如果要提示user,則client端的畫面上呈現confirm
  3. user點了「確定」,則執行該button應該執行的code。點了「取消」,則畫面維持原狀,不做任何處理。

由於confirm是client端的動作,
按照上面的作法就會變成下列步驟
  1. client端的button.onclientclick(這邊用onclientclick來區分是server事件還是client事件)
  2. postback
  3. server端的button.click(sender,e)
  4. 撈DB資料判斷是否要提示
  5. 註冊confirm訊息???????
問題的起源就卡在第5的步驟。
在button.click(sender,e)裡面註冊confirm到button上,已經太晚了,因為已經在server端的階段了,client端的處理在步驟1就已經結束了。
即便confirm出來了,按了確定,又要執行一次該按鈕在server端的code(例如存檔),再次執行server端的button.click(sender,e),可不能再去檢查一次DB資料 再confirm一次。

Suvery Solution

按照前言裡面的步驟,比較有經驗的developer,直覺想法可能是postback兩次,做好程式事件接口來處理這整個client->server->client->server的動作。
這邊就不貼出sample code,只講步驟跟概念:
  1. button上沒有client端事件要處理的事,把最後要存檔的function獨立出來一個private的function。
  2. 點了按鈕,postback,執行button.click(sender,e)
    • 到DB去撈資料,判斷是否要confirm
    • 是,註冊confirm和_dopostback(‘我的postback’,’用得到的DB訊息’)的script到「頁面」上
  3. 第二次postback
  4. confirm訊息,使用者點「確定」
  5. Page_load事件裡面判斷,此次的postback是否由「我的postback」所引起的,(透過eventTarget)
    • 是,則呼叫存檔的function

這樣的作法,的確可行,因為我們需要的是server->client->server,postback兩次的cycle是client->server->client->server。
缺點是得postback兩次,
postback,當頁面一長,控制項一多,viewstate一肥,user頻寬太小,就很容易造成user需等待時間拉長。
有沒有可能不postback兩次呢?

直覺就是想到處理非同步的Ajax。
有了web service與ajax的PageMethods,很多東西都不需要postback來處理了。
於是有了想法,
在Button.onclientclick的時候,呼叫PageMethods,
去DB檢查是否要confirm,如果要,則呼叫confirm,
點了「確定」則不處理,繼續postback,執行server端的button.click(sender,e)
點了「取消」則return false,讓按鈕不postback。

直到我把上面的想法實做成sample code之後,我發現我錯了…

期望的順序是:
  1. client端的button.onclientclick,呼叫PageMethods
  2. PageMethods判斷完DB的資料,return bool
  3. bool若true,則執行client的confirm
  4. if confirm為false,則return false
  5. confirm為true,則執行server端的button.clieck(sender,e)
結果跑出來的順序不一樣:
  1. client端的button.onclientclick,呼叫PageMethods
  2. 執行server端的button.clieck(sender,e)
  3. PageMethods判斷完DB的資料,return bool
  4. bool若true,則執行client的confirm
  5. if confirm為false,則return false
client->PageMethods->server,變成了client->server->PageMethods。
這讓我頗為吃驚,跟原本想像的順序不太一樣。
但我還是很不甘願,決定加工把這樣的問題處理掉,基於PageMethods,還是要達到我們的目的。

Play it!

首先要使用PageMethos該有的設定,請參考:[ASP.NET AJAX]PageMethod:用javascript呼叫server端的method
概念都跟上面的期望順序一樣,
問題點只有在,怎麼樣讓server端的button.click(sender,e)在PageMethods後面執行。
加工的部分:
  1. 用一個hidden來存放PageMethods回傳的bool
  2. button.onclientclick呼叫一個js function檢查hidden的值,來決定要不要confirm
  3. 在textbox onchange事件就呼叫PageMethods。
  4. isPostBack為false的時候,呼叫PageMethods。
  5. 執行server端的button.click(sender,e)後要清掉hidden的值。
附上Sample code,撈資料的方式請自行修改,Sample Code上的方式是根據[修練營ASP.NET]使用Spring.Net輔助切層的專案架構 撈資料的。
.aspx
01<html xmlns="http://www.w3.org/1999/xhtml">
02<head runat="server">
03    <title>未命名頁面</title>
04  
05    <script type="text/javascript">
06    function CallMe(src,dest)
07     {    
08         var ctrl = document.getElementById(src);         
09         PageMethods.IsDataExist(ctrl.value,CallSuccess,CallFailed,dest);
10     }
11   
12       
13     function CallSuccess(res, destCtrl)
14     {    
15         var dest = document.getElementById(destCtrl);         
16         dest.value=res;         
17     }
18       
19       
20     function CallFailed(res, destCtrl)
21     {
22         alert(res.get_message());
23     }
24       
25     function buttonclick()
26     {
27        if($get('HiddenField1').value=='true')
28        {
29            if(!confirm('已經存在資料,確定執行?'))
30            {return false;}}
31        else
32        {return true;}
33     }
34    </script>
35  
36</head>
37<body>
38    <form id="form1" runat="server">
39    <div>
40        <asp:ScriptManager ID="ScriptManager1" runat="server"  EnablePageMethods="true">
41        </asp:ScriptManager>
42        <asp:UpdatePanel ID="UpdatePanel1" runat="server">
43            <ContentTemplate>
44                ID:<asp:TextBox ID="TextBox1" runat="server"></asp:TextBox>
45                <asp:HiddenField ID="HiddenField1" runat="server" />
46                <asp:Button ID="Button1" runat="server" Text="存檔" onclick="Button1_Click" OnClientClick="if(buttonclick()==false){return false;}" />
47                <asp:TextBox ID="TextBox2" runat="server"></asp:TextBox>
48            </ContentTemplate>
49        </asp:UpdatePanel>
50    </div>
51    </form>
52</body>
53</html>

.cs
01protected void Page_Load(object sender, EventArgs e)
02{
03    this.TextBox1.Attributes.Add("onchange", "CallMe('TextBox1','HiddenField1');");
04      
05    if (!IsPostBack)
06    {
07        this.TextBox1.Text = "2";
08        ScriptManager.RegisterStartupScript(this.Page, this.Page.GetType(), "detectID", "CallMe('TextBox1','HiddenField1');", true);
09    }
10}
11[System.Web.Services.WebMethod]
12public static bool IsDataExist(string RegionID)
13{
14    Core.Domain.Interface.IRegion region=(Core.Domain.Region)Core.WebUtility.Repository.Domain("Region");
15      
16    if (RegionID.Length != 0)
17    {
18        region.Id = Convert.ToInt16(RegionID);
19    }
20    else
21    { region.Id = null; }
22    IList regionList = region.GetRegion();
23    if (regionList.Count==0)
24    {
25        return false;
26    }
27    else
28    {
29        return true;
30    }
31}
32protected void Button1_Click(object sender, EventArgs e)
33{
34    this.TextBox2.Text = "存檔成功,ID為 "+this.TextBox1.Text;
35    this.HiddenField1.Value = string.Empty;
36    ScriptManager.RegisterStartupScript(this.Page, this.Page.GetType(), "detectID", "CallMe('TextBox1','HiddenField1');", true);
37}
畫面:(ID 1~4已經存在資料)
1.假設一開始textbox的值從DB撈出來assign給textbox。(測試沒有經過textbox的onchange事件)
1
2.點了存檔後,因為2已經存在DB,所以confirm。
2
3.點了取消,則畫面回到第一張圖。點了確定,則執行server端的button.click。
3

4.再點一次存檔按鈕,則仍顯示confirm,因為DB裡ID為2的資料仍然存在。
4
5.把ID改為5,點存檔,則沒有顯示confirm,直接執行server端的button.click(sender,e)
5

結論

這功能與需求其實並沒有什麼,Sample的情況也相當單純,
但是這個過程讓我發現了很多以前自己的觀念不一定是對的。
在面對與想像中不一樣而產生的bug時,再透過已知的觀念和技巧,去將bug fix掉,
再整理出來分享給大家,
希望大家的收穫可以比我還多。
補充相關連結:http://social.msdn.microsoft.com/Forums/zh-TW/236/thread/d9a39279-7626-4ce5-9cb8-df50d44c04ee