[ASP.net WebForm] 查詢過的值和結果,在切換頁面回來後要保留

Restore Form Value

前言

在多年前本人寫過一篇文章[ASP.net MVC / Html5] WebStorage - localStorage 進階實務應用

如何利用HTML5的localStorage達到「查詢過的值和結果,在切換頁面回來後要保留」的需求

如今相同客戶維護不同系統又遇到同樣需求XD

只不過這次是ASP.net WebForm,須留意的部份為自動產生的hidden欄位:__ViewState、__VIEWSTATEGENERATOR、__EVENTVALIDATION…等等

這幾個欄位如果把值重新回填,ASP.net會報錯

2017.11.16追記

目前已知issue:如果畫面有Ajax連動下拉選單的話,第二層以後的選單可能無法填回資料

2018.4.8 追記:修正checkbox無法還原回勾選問題

1.要先到此文章:JQuery Serialize Method and Checkboxes 把該作者改寫的jQuery serialize方法複製到自己的專案引用

取名為jquery.MySerialize.js

//出處:https://tdanemar.wordpress.com/2010/08/24/jquery-serialize-method-and-checkboxes/
(function ($) {

     $.fn.serialize = function (options) {
         return $.param(this.serializeArray(options));
     };

     $.fn.serializeArray = function (options) {
         var o = $.extend({
         checkboxesAsBools: false
     }, options || {});

     var rselectTextarea = /select|textarea/i;
     var rinput = /text|hidden|password|search/i;

     return this.map(function () {
         return this.elements ? $.makeArray(this.elements) : this;
     })
     .filter(function () {
         return this.name && !this.disabled &&
             (this.checked
             || (o.checkboxesAsBools && this.type === 'checkbox')
             || rselectTextarea.test(this.nodeName)
             || rinput.test(this.type));
         })
         .map(function (i, elem) {
             var val = $(this).val();
             return val == null ?
             null :
             $.isArray(val) ?
             $.map(val, function (val, i) {
                 return { name: elem.name, value: val };
             }) :
             {
                 name: elem.name,
                 value: (o.checkboxesAsBools && this.type === 'checkbox') ? //moar ternaries!
                        (this.checked ? 'true' : 'false') :
                        val
             };
         }).get();
     };

})(jQuery);

實作

1.先新增一個RestoreFormValues.js檔

//出處:http://www.developerdrive.com/2013/08/turning-the-querystring-into-a-json-object-using-javascript/
function QueryStringToJSONObject(queryString) { 
    let pairs = queryString.split('&'); 
    let result = {};
    pairs.forEach(function(pair) {
        pair = pair.split('='); 
        //jQuery .serialize()方法會做encodeURIComponent編碼,所以在此用decodeURIComponent,把資料還原回來
        let key = decodeURIComponent(pair[0]);
        let value = decodeURIComponent(pair[1] || ''); 
        result[key] =  value;
    });

    return JSON.parse(JSON.stringify(result));

}

//出處:http://stackoverflow.com/questions/9807426/use-jquery-to-re-populate-form-with-json-data
function RestoreFormValues(jsonObj) {

    // reset form values from jsonObject
    $.each(jsonObj, function(name, val) {
        let $el = $('[name="' + name + '"]');
        let type = $el.attr("type");
 
        switch (type) {
            case 'checkbox':   
                val = (val==="true")?true:false;  
                $el.prop("checked", val).attr("checked",val); 
                break;
            case 'radio':
                $el.filter('[value="' + val + '"]').prop("checked", true).attr('checked', true);
                break;
            default://可能是<input type='text'>或undefined的<textarea>、<select> 
                $el.val(val);
                break;
        }//end switch
    });

}

//把表單查詢過的條件值填回去,並再查詢
function RestoreForm(name , HttpMethod) {
 

    //此localStorage[name] 有存在
    if (localStorage[name] !== undefined && HttpMethod === "GET")//只要Get Method填回查詢條件即可,post不用
    { 
        
        //encodeURIComponent過的queryString
        let queryString = String(localStorage[name]); 
        let jsonObj = QueryStringToJSONObject(queryString); 
        RestoreFormValues(jsonObj);//把值填回表單
           

        //找「查詢」按鈕
        let $searchBtn = $("form:first #btnQuery");
        if ($searchBtn.length > 0)
        { //有抓到查詢按鈕    
            $searchBtn.click();//點擊它,顯示資料
        }//end if   
    }//end if 

}//end function

2.在網頁的Code-Behind寫下PageTitle

    public partial class MyDemo : System.Web.UI.Page
    {
       
        protected string PageTitle = "此網頁標題";
 
        protected void Page_Load(object sender, EventArgs e)
        {
        }//end Page_Load Event
 
    }

3. .aspx網頁寫法↓

<%@ Page Language="C#" AutoEventWireup="true" 
CodeBehind="MyDemo.aspx.cs" 
Inherits="MyDemo" %>

<!DOCTYPE html>
<html>
<head id="Head1" runat="server"> 
    <title><%= PageTitle %></title>
    <meta name="viewport" content="width=device-width" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> 
     
</head>
<body>
    <form id="form1" runat="server">
       <!--兩個查詢條件-->
       <asp:TextBox runat="server" ID="txtProduct" /><br/>       
       <asp:CheckBox runat="server" ID="cbState_Active_1" Text="啟用" /><br/>

       <!--加上OnClientClick-->
        <asp:Button ID="btnQuery" runat="server" 
         OnClientClick="return myConfirm();"
         OnClick="btnQuery_Click" Text="查詢"
       /><br/>
                
        <!--依自己實際工作需求設定GridView-->
        <asp:GridView ID="gvResult" runat="server"   
                      AllowPaging="True" AllowSorting="True"
                       AutoGenerateColumns="False" DataKeyNames="Sysid"  
                        > 
        </asp:GridView>
                     

    <!--引用jQuery核心-->
    <script type="text/javascript" src="JS/jquery-3.2.1.js"></script> 
     <!-- 改寫過的jQuery serialize() -->
    <script type='text/javascript' src='JS/jquery.MySerialize.js' ></script> 
    <!--把localStorage裡的表單值填回表單-->
    <script type="text/javascript" src="JS/RestoreFormValues.js"></script> 
    <script type="text/javascript"> 
        //把網頁標題當做此頁localStorage的Key,才不會和其他頁面衝突
        let localStorageName = "<%= PageTitle %>";
        function myConfirm()
        {
            //查詢參數表單
            let myForm = $("form:first :not([type=hidden][name^=__])").serialize({ checkboxesAsBools: true });//queryString字串  
                //排除ASP.net WebForm自動產生的hidden欄位(__VIEWSTATE,__VIEWSTATEGENERATOR,__EVENTVALIDATION)
                //因為 不可回填給原本欄位,否則ASP.net WebForm會報錯 
                 
            localStorage[localStorageName] = myForm;//把QueryString存入localStorage
              
            return true;//提交表單
        }
           
        $(function () {  

            RestoreForm(localStorageName,"<%= Request.HttpMethod %>"); 
           
        });//end function
    </script>
  
    </form>
</body>
</html>

然後登出時↓

 
        
     
           
                    //表單驗證登出
                    FormsAuthentication.SignOut();
                    //清空Session
                    Session.Clear();
                    
                    //取得登入頁Url
                    //視Web.config裡<forms>的設定
                    string loginUrl = FormsAuthentication.LoginUrl;
                    //清除此網站的localStorage
                    string js = @"localStorage.clear();"; 
                    //window.location.href也可以,視自己網站有沒有嵌入iframe狀況來調整
                    js += " top.location.href='" + loginUrl + "';";//導回登入頁
                    ClientScript.RegisterStartupScript(Page.GetType(),Guid.NewGuid().ToString(),js, true);
                
       

Web.config裡的loginUrl要設定

<system.web>
  <authentication mode="Forms">
      <forms loginUrl="~/Login.aspx" name="MyDemoWebSite"  
             defaultUrl="~/Index.aspx"  
             timeout="60"  
             slidingExpiration="true"/>
  </authentication> 
</system.web>