我在做采購系統時,因為使用了ASP.NET AJAX的UpdatePanel的控件,可以使得頁面局部刷新顯示。但是使用起來問題還是很多。
下面列出了一種情況,花了將近5個小時才算解決,雖然不是很完美,但是對於一般應用應該夠了。
應用背景:在采購系統里,因為一個產品可以有多個供應商,同樣一個供應商可以有多個產品。所以產品和供應商是多對多的關系。
在產品頁面,用戶選擇一個產品,我們希望他可以選擇供應商。如下圖,選擇“得利紙業”,然后在單擊“關聯供應商”,就彈出供應商頁面,讓用戶選擇供應商。
其實,如果是采用刷新技術,這種功能很好實現。讓父頁面刷新一下,解決方法還是非常簡單的。常規的解決方法大都是用 window.open()彈出子頁面,
然后在子頁面關閉時,刷新父頁面就完成了。但是,因為父頁面全刷新,用戶體驗非常差。
剛開始,我感覺問題很簡單,bootstrap有model模板(詳見 http://v3.bootcss.com/javascript/) ,代碼類似如下
<a href="#" class="btn btn-sm bg-green btn-flat" data-toggle="modal" data-target=".bs-example-modal-lg">關聯供應商</a>
用戶單擊關聯供應商時,彈出在產品列表頁面的一個層。(注意:是層,所以這里沒有父-子頁面,為的就是局部刷新),代碼類似如下
<asp:UpdatePanel ID="UpdatePanel5" runat="server"> <ContentTemplate> <div class="modal fade bs-example-modal-lg" tabindex="-1" role="dialog" aria-labelledby="myLargeModalLabel" aria-hidden="true"> <asp:linkbutton Text="分類A" runat=server id="c1" /> <asp:linkbutton Text="分類B" runat=server id="c21" /> <asp:gridview runat="server" id="prod"></asp:gridview> <asp:Button ID="btn_connection_supply" runat="server" Text="關聯供應商" OnClick="btn_connection_supply_Click" /> </div> </div> </ContentTemplate> </asp:UpdatePanel>
彈出的層,放到UpdatePanel里,在UpdatePanel,左邊是ASP.NET LinkButton顯示分類名稱,右邊是GridView顯示分類結果,下部一個Button,單擊“關聯供應商”,
感覺很完美。但是實際測試,問題來了。當用戶單擊左邊分類名稱時,白色彈框消失了,用戶根本沒有時間選擇右邊的商戶列表,更麻煩的是白色消失后,彈出的陰影背景還在,整個頁面像死在那里了。只有重新刷新頁面。
問題出現了,就開始想辦法解決。首先想,用戶單擊左邊Linkbutton,因為是異步回發,最簡單的方式,是在回發回來時,仍然讓白色的供應商頁面顯示。查了一下Bootstrap,發現有函數
Bootstrap提供了show函數,可以顯示調用,讓彈窗顯示。
因此,我在LinkButton里,調用了此方法,代碼類似如下
ScriptManager.RegisterStartupScript(button, typeof(LinkButton), "init", "$(".pop").show(true);", true);
很奇怪,跟蹤了返回解決,發現雖然服務器執行了,客戶端沒有執行。我記得我在做日志系統時,這個代碼是執行了,唯一的區別是:
在采購系統里,我將UpdatePanel的 ChildrenAsTriggers設置為"False" UpdateMode設置"Conditional" ,為的就是不讓子刷新時,父跟着刷新。
看了Bootstrap無法解決了,只能夠通過ScriptManager解決, Sys.WebForms.PageRequestManager提供了一系列的Request客戶端頁面生命周期,詳見
https://msdn.microsoft.com/zh-cn/library/bb311028.aspx
所以,我在LinkButton的返回時,執行endRequest
var prm = Sys.WebForms.PageRequestManager.getInstance(); prm.add_endRequest(function () { $(".model").show(true) });
然而,這種情況得到了相反的結果,白色頁面關不掉了。正常情況下,用戶在單擊“確認管理供應商”時,頁面就應該關閉,現在又關不掉了。
看來,我還要區分endrequest的請類似,其另外一種方法是:應該可以判斷用戶提交的Button類型。函數原型如下. 其實這里還有一個問題沒有解決,如果GridView供應商分頁,同樣單擊上一頁,下一頁,也會出現這個問題。看來問題越來越多。
var prm = Sys.WebForms.PageRequestManager.getInstance(); prm.add_endRequest(function (send,arg) { $(".model").show(true) });
此時,我已經精疲力盡,算了,不做了,去吃飯了。
吃過飯后,突然想出來一個方法:為什么要服務器分頁,不是有JS 的DataTable,以前用過他,他本身就可以客戶端分頁啊。
去http://www.datatables.net/ 看一下演示(注:DataTable默認使用Google提供Jquery CDN,因為大陸屏蔽了Google,所以演示站點無法打開,可以在Host里增加配置解決。)
dataTable默認提供客戶端分頁,只要table增加一個id,就可以自動分頁,排序,模糊查詢
$('#tbl_supply2').DataTable( { } )
可是DataTable,就在我認為已經好的時候,問題又來了。再我測試時,選擇了產品,單擊“關聯供應商”時,系統調用 $('#tbl_supply2').DataTable() 。但是可能用戶沒做任何操作關閉了彈窗,又再次單擊了“關聯供應商”。
此處會出現一個錯誤“DataTable已經被初始化”。原來用戶第一次單擊關聯供應商時,DataTable進行了初始化。再次單擊時,因為已經初始化了,所以會出錯。這個問題解決起來會比較簡單,只要在document.Ready()里進行進行初始化就可以了。經過測試發現,因為在產品列表里,也用了AJAX,在異步回發時,頁面結構已經改變,所以Ready()事件里執行會有問題。
趕快看看DataTable的接口。后來在其API里提供里找到了相關介紹 http://datatables.net/reference/option/destroy 可以利用其destroy參數
代碼只要更改為:
$('#tbl_supply2').DataTable( { destroy: true } )
就可以了。
因此,最后的函數如下:增加了Ordering參數,禁止排序,lengthMenu設置分頁為5或者10。
function pageSupply() { $('#tbl_supply2').DataTable( { destroy: true, "ordering": false, "info": false, "lengthMenu": [5, 10] } ) };
最后的頁面就是完成的頁面 可以去演示 http://demo.dotnetcms.org/purchase/ 查看。 另外,說說不完美的,因為是客戶端分頁,所以大量數據可能會影響性能。
以前給一個客戶端,他們有2000多個用戶列表使用DataTable分頁,用戶打開時,2000多行分頁經常會卡。不過,在采購系統里,應該不會有2000個供應商,對於應付一二百個
供應商,DataTable 客戶端分頁排序,還是綽綽有余的。