BloggerAds

2013年8月29日 星期四

[Ruby On Rails] 使用 Excel gem roo 時,出現 LoadError (cannot load such file -- zip/zipfilesystem) 錯誤的解決方式

這個問題發生的原因為roo在require 'rubyzip'時,並沒有指定rubyzip的版本。

而rubyzip現在(2013/8/30)已更新成版本1.0.0。1.0.0就不再有zip/zip、zip/zipfilesystem 這兩個檔案。

所以解決方法是在Gemfile加入版本指定即可:

 gem 'rubyzip', "~> 0.9.9"

2012年1月18日 星期三

(Chrome、FireFox) JavaScript設定值給TextBox時,無法觸發PostBack的一種解法


如果有使用.NET 中TextBox的OnTextChanged事件,希望在TextBox的文字變動後,在Server端做一些事,通常會像下方這樣用:
ex. <asp:TextBox ID="txt1" runat="server" OnTextChanged="txt1_TextChanged" AutoPostBack="True" />

這個方式在人為操作的狀況下並不會有什麼問題,修改TextBox中的字後,會PostBack去執行程式。但如果修改TextBox中的文字,是使用JavaScript去設定值的話(ex. txt1.value = 'abc'),在各瀏覽器卻會有不同的結果產生,IE 會正常的執行 PostBack,但在Chrome、FireFox卻無法正常的觸發PostBack!

在試過jQuery的一些function後(ex. bind、on、trigger),發現還是沒辦法解決這個問題,最後只好用一個偏方來處理,使用setInterval一段時間就檢查一次TextBox的值,發現值有變動,就執行PostBack,解法如下:

$(document).ready(function() {          
    //非IE才設Interval,因為IE可正常觸發
    if(navigator.userAgent.indexOf("MSIE")==-1) {
        setInterval('detectValueChange();', 500);
    }
});
   
function detectValueChange() {
    currentValue = $('#<%=txt1.ClientID%>').val();
    oldValue = $('#<%=hid1.ClientID%>').val();
         
    if (currentValue != oldValue) {
        //使用<asp:HiddenField>暫存值,讓PostBack後,值還存在 
        $('#<%= hid1.ClientID%>').val(currentValue);
        __doPostBack('<%= txt1.ClientID%>', '');              
    }  
}       

2011年11月9日 星期三

[C#]在有驗證控制項的狀態下幫Button加上confirm (OnClientClick +Validator)


如果要對一個asp:Button控制項做再次詢問的動作(ex. 確認是否刪除? ),一般來說是可以直接這樣用:


<asp:Button ID="btnSubmit" runat="server" Text="OK" onclick="btnSubmit_Click"
    CausesValidation="true" OnClientClick="return confirm('are you sure?');"/>


而在有驗證控制項的狀況下,若還是用上面的return confirm方法,可是會讓驗證控制項失效的,所以要改成下列這種用法:

<asp:Button ID="btnSubmit" runat="server" Text="OK" onclick="btnSubmit_Click"
    CausesValidation="true" OnClientClick="if (Page_ClientValidate('[驗證群組]')) { return confirm('are you sure?');}"/>

其中 [驗證群組] 要記得改成你所設定的 ValidationGroup 

2011年11月1日 星期二

Google Analytics API called by C#

這是可以透過Google Analytics API 去抓出你網站的拜訪資料的功能,主要是送一個Request,再接收XML檔案,下面分兩段說明,首先是抓回XML的部份,:
(以下內容是參考 http://www.dotblogs.com.tw/lastsecret/archive/2010/10/06/18157.aspx )
private void GetXml() {  
        //送出request的網址
        var request = (HttpWebRequest)HttpWebRequest.Create(
            //在這是取國家的訪客數,參數可參考http://code.google.com/intl/zh-TW/apis/analytics/docs/gdata/gdataReferenceDataFeed.html#maxResults
            string.Format("https://www.google.com/analytics/feeds/data?"+                        
                            "ids=ga%3A{0}&dimensions=ga%3Acountry" +
                            "&metrics=ga%3Avisits&" +
                            "start-date={1}&end-date={2}&sort=-ga%3Avisits&max-results=10",
                            "12345678",  //Google Analytics 網站的ID
                            "2011-09-24",
                            DateTime.Now.ToString("yyyy-MM-dd")));

        //在header必須帶入token,token的取得請看下方的方法
        request.Headers.Add("Authorization", "GoogleLogin auth=" + GetAuthToken());

        try {
            var response = request.GetResponse();

            var responseContent = new StreamReader(response.GetResponseStream()).ReadToEnd();

            //抓到XML, 存到暫存資料夾
            StreamWriter rd = File.CreateText(Server.MapPath("~/TempFile") + @"\Analytics_Country.xml");
            rd.Write(responseContent);
            rd.Flush();
        } catch (Exception ex) {
            Javascript.Alert(this.Page, "Error:" + ex.ToString());
        }
}

private static string GetAuthToken() {
        var postContent = string.Format(@"accountType=GOOGLE&Email={0}&Passwd={1}&service=analytics&source={2}",
                                         "Account", //Google 同號
                                         "PWD", //Google 密碼
                                         "1234"//隨便
                                        );

        byte[] bPostData = Encoding.ASCII.GetBytes(postContent);

        //參數與用法
        //參考http://code.google.com/intl/zh-TW/apis/accounts/docs/AuthForInstalledApps.html
        HttpWebRequest req = (HttpWebRequest)HttpWebRequest.Create("https://www.google.com/accounts/ClientLogin");
        req.Method = "POST";
        req.ContentType = "application/x-www-form-urlencoded";
        req.ContentLength = bPostData.Length;

        var reqStream = new StreamWriter(req.GetRequestStream());

        reqStream.Write(postContent);
        reqStream.Flush();

        HttpWebResponse rep = (HttpWebResponse)req.GetResponse();
        StreamReader repStream = new StreamReader(rep.GetResponseStream(), Encoding.UTF8);
        string ctx = repStream.ReadToEnd();

        //送出的response抓取token的值
        return Regex.Split(ctx, "Auth=", RegexOptions.IgnoreCase)[1];
     
    }  
再來是抓XML中的資料:
(以下內容是參考http://www.reimers.dk/jacob-reimers-blog/added-google-analytics-reader-for-net)
protected void GetData(object sender, EventArgs e) {
        //讀XML檔案
        XDocument xmlDoc = XDocument.Load(Server.MapPath("~/TempFile") + "/Analytics_Country.xml");
        XNamespace defaultSpace = xmlDoc.Root.GetDefaultNamespace();
        XNamespace dxpSpace = xmlDoc.Root.GetNamespaceOfPrefix("dxp");
     
        //LINQ  XML    
        var datas = from entry in xmlDoc.Root.Descendants(defaultSpace + "entry")
                    select new {
                        Country = entry.Element(dxpSpace + "dimension").Attribute("value").Value,
                        Value = entry.Element(dxpSpace + "metric").Attribute("value").Value
                    };
     
        foreach (var entry in datas) {          
            Response.Write("Country:" + entry.Country);
            Response.Write("Visits: " + entry.Value + "<br />");
        }
        //datas.ToDictionary<string, int>(m => m.Country, m => m.Value);
    }

2011年8月29日 星期一

ASP.NET的WebService + autocomplete + tagit 合體

在使用Google+、Facebook時,會常常看到Tag這個東西,這東西在使用上,很方便,而且長的還蠻好看的,我很喜歡這操作很直覺的東西,所以上網找了些plugin,並拿來實作,而我找了 tagit 這個plugin,還算簡單使用(而且我認為這是我找的 plugin 當中最漂亮的...)。


一般的使用很簡單,照著 tagit 的 example就可以用了,但是一般的tag通常都會加上自動完成(AutoComplete)功能,所以不加個AutoComplete感覺就很弱,下面就提供個範例是用C#的WebService來實作AutoComplete功能(PHP很簡單,下面會提到一點):


1.在WebService部份,我們用DataTable來傳回傳,所以可以在asmx.cs這樣寫:
[WebMethod]
public DataTable GetList(string prefixText) {  
        DataTable dt = new DataTable();
        //從資料庫查詢資料的語法,請自發揮...     
        // ......略
        return dt;    //最後return一個DataTable
}
2.在aspx部份,用一個TextBox:
<asp:TextBox ID="txtTag"  runat="server" autocomplete="off"  />
3.再來就是JavaScript的實作了,通常是在document.ready時實作
$(document).ready(function() {
    $("#<% =txtTag.ClientID %>").tagit({
        allowSpaces: true,
        tagSource: function(request, response) {
            $.ajax({
                url: "/GetList.asmx/GetList", //步驟1的WebService網址及Function名稱
                data: { prefixText: request.term }, //要傳入WebService Function的變數,這裡是用來抓user所輸入的內容
                dataType: "xml",  //dataType設成Xml,php直接用json,再搭配json_encode來產生資料就可以了
                type: "POST",
                contentType: "application/x-www-form-urlencoded; charset=utf-8",    //這裡很重要,要把contentType改成WebService預設回傳的application/x-www-form-urlencoded
                dataFilter: function(data) { return data; },
                success: function(data) {    //成功抓到資料後,要執行的動作
                    response($.map($(data).find("[欄位名稱]"), function(item) {  //因為是XML所以用這個方式來取資料,[欄位名稱]請記得更改成DataTable的欄位名稱
                        return { value: $(item).text() }
                    }));
                },
                error: function(XMLHttpRequest, textStatus, errorThrown) {
                    alert(textStatus + ":" + errorThrown);  //Debug用
                }
            });
        }         
    });
4.AutoComplete的部份完成!


5.如果要再龜毛點,限制使用者必須輸入DataTable中的內容的話,就在JavaScript中先加入這一段:
$.expr[':'].textEquals = function(a, i, m) {
        return $(a).text().match("^" + m[3] + "$");
};
6.再來改一下document.ready中tagit的部份,加入onTagAdded 的事件。


7.最後還要為tagit所產生的text加入change事件。


6+7後的JavaScript如下:
$(document).ready(function() {
    $("#<%=txtTag.ClientID %>").tagit({
        allowSpaces: true,
        tagSource: function(request, response) {
            $.ajax({
                url: "/GetList.asmx/GetList",
                data: { prefixText: request.term },
                dataType: "xml",
                type: "POST",
                contentType: "application/x-www-form-urlencoded; charset=utf-8",
                dataFilter: function(data) { return data; },
                success: function(data) {
                    response($.map($(data).find("[欄位名稱]"), function(item) {
                        return { value: $(item).text() }
                    }));
                },
                error: function(XMLHttpRequest, textStatus, errorThrown) {
                    alert(textStatus + ":" + errorThrown);
                }
            });
        },
        onTagAdded: function(event, tag) {  
            //注意!別在這alert東西,不然會執行兩次
            //.ui-autocomplete、tagit-label 是tagit執行後預設的class
            if ($(".ui-autocomplete li:textEquals('" + tag.find(".tagit-label").text() + "')").size() == 0) {
                that.removeTag(tag);    //若輸入的內容不在資料庫中,則移除tag
            }
        }
    });


    $(".tagit input").change(function() {
        if ($(".tagit li:textEquals('" + $(this).val() + "')").size() == 0) {
            $(this).val('');  //若輸入的內容不在資料庫中,則清空TextBox的內容
        }
    });
});
8. 再次大功告成


參考資料:
http://stackoverflow.com/questions/2587378/how-to-implement-mustmatch-and-selectfirst-in-jquery-ui-autocomplete
https://github.com/aehlke/tag-it
http://jqueryui.com/demos/autocomplete/

UserControl 提供事件給網頁(Parent Page)觸發的方式

最近在弄一個讓 UserControl 提供事件給網頁呼叫的東西下面就提供一個範例,來讓UserControl中的 GridView的 RowSelecting 事件提供給網頁(Parent Page)觸發

1.首先,在UserControl的ascx.cs中,宣告一個事件:
public event EventHandler PublicRowSelecting;
2.再來還是在UserControl的ascx.cs中,讓原本的GridView RowSelecting的事件,執行剛剛宣告的事件PublicRowSelecting:
protected void gvMain_RowSelecting(object sender, GridViewSelectEventArgs e) {                             
            this.PublicRowSelecting(this, new EventArgs());       
}
3.接下來就是在網頁中使用了,在網頁(Parent Page)的aspx.cs 直接設定事件
UserControl1.PublicRowSelecting += new EventHandler(UserControl1_PublicRowSelecting);
4. UserControl1_PublicRowSelecting 是你要執行的程式,這個範例只要 Response.Write就好了

protected void UserControl1_PublicRowSelecting(object sender, EventArgs e) {
      Response.Write("Test OK");
}
5.大功告成,接下來只要執行到 UserControl中GridView的RowSelecting事件,就會執行UserControl1_PublicRowSelecting了。

2011年8月23日 星期二

.Net Web Service Internal Server Error 錯誤處理方式


在.Net 使用Web Service的方式很簡單,只要使用下列code就可以使用

首先在aspx加上如下程式:
<asp:ScriptManager runat="server" ID="ScriptManager1">
    <Services>
        <asp:ServiceReference Path="~/AAA.asmx" />
    </Services>
</asp:ScriptManager>
JavaScript即可用下列方式使用:
Class名稱.function名稱(參數, 完成時執行的function名稱, 錯誤時執行的function名稱, TimeOut時執行的function名稱);
ex.
AAA.funAAA(strB, OnComplete, OnError, OnTimeOut)

但若出現「Internal Server Error」,往往會讓人摸不著頭緒,很難找原因。在這提供幾個參考的debug方式:

1.先檢查程式中Web Service的路徑(URL)是否有錯誤,相對路徑與絕對路徑的使用是否都抓到asmx檔案。

2.Web Service是否有正確回傳資料,可直接打上網址 (ex. http://localhost/AAA.asmx) 並直接輸入參數檢查資料是否正確,或是否有錯誤發生。

3.若WebService 無法用遠端存取,只可在本機(Local)存取 ,則要在web.config加上下列藍色的部份:
<configuration>
    <system.web>
        <webServices>
            <protocols>
                <add name="HttpPost"/>
                <add name="HttpGet"/>
            </protocols>
        </webServices>

    </system.web>
</configuration>