Применение jQuery-UI-dialog в ASP.NET
Недавно мне попался проект, в котором заказчик возжелал применения jQuery-UI диалогов аж в трех различных ипостасях. Я решил поделиться с народом своими шаблонами решения этих проблем.
1.Диалог-намордник, запрашивающий логин/пароль.
В рамках технологии ASP.NET есть множество механизмов для решения проблемы всплывающего диалога, который должен закрыть некую страничку от посторонних. Эта задача может быть решена и на уровне IIS (с помощью базовой аутентификации и конструкции DENY в конфиге), это же можно решить на MS AJAX. Наиболее простой вариант - серверное решение с помощью MultiView, Panel и так далее. Но здесь попался большой любитель клиентского функционала - и jQuery так или иначе уже был задействован практически на каждой страничке проекта.
Для создания дополнительного клиенского диалога, запрашивающего логин-пароль, как таковая страничка ASP.NET с тегом Form и классом не нужна. Достаточно простой html-странички. Но в данном случае я сделал ASPX - это не имеет значения, ибо такая страничка-намордник серверного функционала не имеет.
Обратите внимание, что безопасность решения не нарушается (ибо пароли согласно идеологии интернета и так ходят в открытом виде) - я их просто кинул как QueryString для странички ProtectedPage, куда не следуют заходить кому попало. В принципе это решение подвержено атаке на подбор пароля, однако существует множество механизмов защиты от нее - как на этой же страничке, так и на сервере.
Код странички ProtectedPageLogin (требующей логин-пароль для входа на страничку ProtectedPage выглядит совершенно бесхитростно:
1: <%@ Page Language="VB" AutoEventWireup="false" CodeFile="ProtectedPageLogin.aspx.vb" %>
2:
3: <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
4:
5: <html xmlns="http://www.w3.org/1999/xhtml">
6: <head runat="server">
7: <title>FlySeason.RU - бронирование чартерного авиабилета</title>
8: <link rel="stylesheet" type="text/css" href="css/excite-bike/jquery-ui-1.8.14.custom.css" />
9: <script type="text/javascript" language="javascript" src="jquery-1.6.1.min.js"></script>
10: <script src="jquery-ui-1.8.14.custom.min.js" type="text/javascript"></script>
11: <script type="text/javascript" language="javascript">
12:
13: $(document).ready(function () {
14: var $dialog1 = $('#login-dialog')
15: .dialog({
16: modal: true,
17: autoOpen: true,
18: resizable: false,
19: draggable: true,
20: buttons: {
21: Ok: function () {
22: $(this).dialog("close");
23: $.ajax({
24: 'url': 'ProtectedPage.aspx',
25: 'data': { 'l': $('#txLogin')[0].value, 'p': $("#txPass")[0].value },
26: 'dataType': 'json',
27: 'type': 'POST',
28: 'success': window.location.href = 'ProtectedPage.aspx'
29: });
30:
31: },
32: Close: function () {
33: $(this).dialog("close");
34: window.location.href = 'Default.aspx';
35: }
36: }
37:
38: });
39: });
40:
41:
42: </script>
43: </head>
44: <body>
45: <form id="form1" runat="server">
46:
47: <div style="visibility: hidden;">
48: <div id="login-dialog">
49: <p>
50: <span class="ui-icon ui-icon-circle-check" style="float: left; margin: 0 7px 50px 0;">
51: </span><b>Пожалуйста войдите:</b>
52: </p>
53: <table cellspacing="2px">
54: <tr>
55: <td>
56: Логин:
57: </td>
58: <td>
59: <asp:TextBox ID="txLogin" runat="server"></asp:TextBox>
60: </td>
61: </tr>
62: <tr>
63: <td>
64: Пароль:
65: </td>
66: <td>
67: <asp:TextBox ID="txPass" runat="server" TextMode="Password"></asp:TextBox>
68: </td>
69: </tr>
70: </table>
71: <br /><br />
72: </div>
73: </div>
74: </form>
75: </body>
76: </html>
На защищаемой от посторонних страничке я сделал вот такую проверочку.
1: Partial Class ProtectedPage
2: Inherits System.Web.UI.Page
3:
4: Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
5: If Not IsPostBack Then
6: If Request.Form("l") IsNot Nothing And Request.Form("p") IsNot Nothing Then
7: If Request.Form("l").Trim <> "" And Request.Form("p").Trim <> "" Then
8: If CheckUser(Request.Form("l"), Request.Form("p")) Then
9: Session("ProtectedPageLogin") = Request.Form("l")
10: Session("ProtectedPagePass") = Request.Form("p")
11: FormsAuthentication.SetAuthCookie(Request.Form("l"), True)
12: End If
13: Else
14: Response.Redirect("ProtectedPageLogin.aspx")
15: End If
16: Else
17: If Request.IsAuthenticated Then
18: CheckUser(Session("ProtectedPageLogin"), Session("ProtectedPagePass"))
19: End If
20: End If
21: Else
22: If Request.IsAuthenticated Then
23: CheckUser(Session("ProtectedPageLogin"), Session("ProtectedPagePass"))
24: End If
25: End If
26: End Sub
27:
28: Function CheckUser(ByVal Login As String, ByVal Pass As String) As Boolean
29: Dim db1 As New FlySeasonDataContext
30: Dim User = (From X In db1.ProtectedPage Where X.Login = Login And X.Password = Pass Select X).ToList
31: If User.Count > 0 Then
32: Return True
33: Else
34: Response.Redirect("ProtectedPageLogin.aspx")
35: End If
36: End Function
37: End Class
Поскольку эта страничка по большому счету не представляет никакой ценности (в данном случае это прсто журнал заказов: заказ принят, заказ в работе, заказ завершен) - я даже опустил проверку на атаку подбором пароля. О более серьезных методах защиты страничек от посторонних взглядов вы можете почитать здесь - Безопасность Web-приложений.
2.Всплывающее окно-предупреждение.
Теперь рассмотрим другой вариант всплывающей формы - когда надо одноразово сообщить что-то юзеру - например "Вам пришло новое сообщение", "Вам оформлен заказ номер XXX", "Вы уверены?. Для этого серверный код странички выполняет какую-то свою проверку и выставляет какое-то значение в поле HyddenFields : ZakazNum.Value = ZakazNumber(0).Zakaz_ID. Задача клиентского кода - отследить что значение этого триггера непустое и выдать всплывающую форму:
1: <%@ Page Language="VB" AutoEventWireup="false" CodeFile="Ticket.aspx.vb" Inherits="Ticket" EnableEventValidation="false" %>
2:
3: <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
4: <html xmlns="http://www.w3.org/1999/xhtml">
5: <head>
6: <title>FlySeason.RU - бронирование чартерного авиабилета</title>
7: <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
8: <link rel="stylesheet" type="text/css" href="S.css" />
9: <link rel="stylesheet" href="css/excite-bike/jquery-ui-1.8.14.custom.css" type="text/css" media="screen" charset="utf-8" />
10: <script type="text/javascript" language="javascript" src="jquery-1.6.1.min.js"></script>
11: <script src="jquery-ui-1.8.14.custom.min.js" type="text/javascript"></script>
12: <script type="text/javascript" language="javascript">
13: $(document).ready(function () {
14: var $dialog2 = $('#dialog-message-2')
15: .dialog({
16: modal: true,
17: autoOpen: false,
18: resizable: false,
19: draggable: true,
20: buttons: {
21: OK: function () {
22: $(this).dialog("close");
23: $("#ZakazNum")[0].value="";
24: }
25: }
26: });
27:
28: if ($("#ZakazNum")[0].value != ""){
29: $("#ZakazN")[0].innerHTML = $("#ZakazNum")[0].value;
30: $dialog2.dialog('open');
31: }
32: });
33:
34: </script>
35: </head>
36: <body style="background-color: #FFFFFF; margin: 0px;">
37: <form id="form1" runat="server">
38: <asp:HiddenField ID="ZakazNum" ClientIDMode="Static" runat="server" />
39: ...
40: <div id="dialog-message-2">
41: <p style="margin:10px;">
42: <span class="ui-icon ui-icon-circle-check" style="float: left; margin: 0 7px 50px 0;">
43: </span><b>Спасибо, Ваш заказ принят </b>
44: </p>
45: <p style="margin:10px;">
46: <span class="ui-icon ui-icon-circle-check" style="float: left; margin: 0 7px 50px 0;">
47: </span> Ваш номер заказа: <b><span id="ZakazN"></span></b>
48: </p>
49: <p style="margin:10px;">
50: Для ускорения обработки подтвердите пожалуйста Ваш заказ по телефону
51: <br />
52: <center><b>8(495)-648-80-88</b></center>
53: </p>
54: </div>
55: ...
56: </form>
57: </body>
58: </html>
Как видите, тут все тоже элментарно - клиентский код увидел взведенный триггер (в строке 38) и выдал форму предупреждения. Потом почистил триггер за собой.
3.Всплывающая jQuery форма при нажатии на кнопку.
И наконец, расмотрим третий (более интересный) вариант. На форме много кнопок (например "заказать этот товар") - при клике на кнопке надо выдать jQuery UI диалог принимающий заказ на товар. Это наиболее интересный вариант - потому что тут уже надо понимать пару моментов:
- Во-первых, Jquery UI перемещает свой диалог за пределы тегов FORM - поэтому чтобы что-то передать в постбек движка ASP.NET - надо создать HyddenFields и скопировать в него то, что юзер введет в диалог jQuery UI (строки 23-27).
- Во-вторых, надо убедится что ASP.NET постбек вообще присутсвует на страничке. Для этого надо посмотреть обьект window - как видите функция постбека присутсвует. Можно даже посмотреть на какой Input был навешен весь асповский механизм постбека.
- В-третьих - поскольку товаров на форме как правило не одна штука, надо организовать передачу параметра - чтобы понять на какой из кнопок был произведен клик. В данном случае кнопки у меня имеют класс .abutton - я добавил (строки 37-46) всплывающую по клику форму (строки 14-35) - на все элементы с этим классом. А в обработчике клика я вытащил ID товара (стрки 38-42), который у меня был где-то на форме неподалеку от кнопки и этот код товара тоже положил в HyddenFilds (туда же, куда полоил все что ввел юзер для заказа). Таким образом появилась возможность отпарвить на форму обычный постбек.
- И наконец последняя часть этой головоломки - как принять этот постбек. Ведь я его отправил без имени элемента Input и асповский конвеер просто не знает какой button_click вызвать.
1: <%@ Page Language="VB" AutoEventWireup="false" CodeFile="Ticket.aspx.vb" Inherits="Ticket" EnableEventValidation="false" %>
2:
3: <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
4: <html xmlns="http://www.w3.org/1999/xhtml">
5: <head>
6: <title>FlySeason.RU - бронирование чартерного авиабилета</title>
7: <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
8: <link rel="stylesheet" type="text/css" href="S.css" />
9: <link rel="stylesheet" href="css/excite-bike/jquery-ui-1.8.14.custom.css" type="text/css" media="screen" charset="utf-8" />
10: <script type="text/javascript" language="javascript" src="jquery-1.6.1.min.js"></script>
11: <script src="jquery-ui-1.8.14.custom.min.js" type="text/javascript"></script>
12: <script type="text/javascript" language="javascript">
13:
14: $(document).ready(function () {
15: var $dialog = $('#dialog-message')
16: .dialog({
17: modal: true,
18: autoOpen: false,
19: resizable: false,
20: draggable: true,
21: buttons: {
22: 'Заказать': function () {
23: $("#hName")[0].value = $("#txName")[0].value;
24: $("#hTel")[0].value = $("#txTel")[0].value;
25: $("#hMail")[0].value = $("#txMail")[0].value;
26: $("#hKol")[0].value = $("#txKol")[0].value;
27: $("#hPromo")[0].value = $("#txPromo")[0].value;
28: __doPostBack();
29: $(this).dialog("close");
30: },
31: 'Нет': function () {
32: $(this).dialog("close");
33: }
34: }
35: });
36:
37: $('.abutton').click(function () {
38: var tr = $(this).parent()[0];
39: var tbody = $(tr).parent()[0];
40: var trs = $(tbody).find('tr');
41: $("#hPrice")[0].value = $(trs).find('td').find('span')[0].title
42: $("#hID")[0].value = $(this).find('input')[0].value;
43: $dialog.dialog('open');
44: // prevent the default action, e.g., following a link
45: return false;
46: });
47: });
48:
49: </script>
50: </head>
51: <body style="background-color: #FFFFFF; margin: 0px;">
52: <form id="form1" runat="server">
53: <asp:HiddenField ID="ZakazNum" ClientIDMode="Static" runat="server" />
54: <asp:HiddenField ID="hName" ClientIDMode="Static" runat="server" />
55: <asp:HiddenField ID="hTel" ClientIDMode="Static" runat="server" />
56: <asp:HiddenField ID="hMail" ClientIDMode="Static" runat="server" />
57: <asp:HiddenField ID="hKol" ClientIDMode="Static" runat="server" />
58: <asp:HiddenField ID="hPromo" ClientIDMode="Static" runat="server" />
59: <asp:HiddenField ID="hID" ClientIDMode="Static" runat="server" />
60: <asp:HiddenField ID="hPrice" ClientIDMode="Static" runat="server" />
61: ...
62: <div style="visibility: hidden">
63: <div id="dialog-message">
64: <p style="margin:10px;">
65: <span class="ui-icon ui-icon-circle-check" style="float: left; margin: 0 7px 50px 0;">
66: </span><b>Этот билет есть в наличии</b>
67: </p>
68: <table border="0" cellpadding="5">
69: <tr><td>Ваше имя:</td><td>
70: <asp:TextBox ID="txName" ClientIDMode="Static" runat="server"></asp:TextBox></td></tr>
71: <tr><td>Телефон:</td><td>
72: <asp:TextBox ID="txTel" ClientIDMode="Static" runat="server"></asp:TextBox></td></tr>
73: <tr><td>Email:</td><td>
74: <asp:TextBox ID="txMail" ClientIDMode="Static" runat="server"></asp:TextBox></td></tr>
75: <tr><td>Нужно билетов:</td><td>
76: <asp:TextBox ID="txKol" ClientIDMode="Static" runat="server"></asp:TextBox></td></tr>
77: <tr><td> </td><td> </td></tr>
78: <tr><td>(*) PROMO-CODE:</td><td>
79: <asp:TextBox ID="txPromo" ClientIDMode="Static" runat="server"></asp:TextBox></td></tr>
80: </table>
81:
82: <p style="margin:10px;" >
83: (*) Для получения индивидуальной скидки укажите пожалуйста свой код карты FlyBonus.
84:
85: </p>
86: </div>
87: </form>
88: </body>
89: </html>
1: Partial Class Ticket
2: Inherits System.Web.UI.Page
3:
4: Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
5: If Not IsPostBack Then
6: ...
7: Else
8: '__EVENTTARGET = undefined (этот jQuery постбек обрабатывается прямо тут
9: '__EVENTTARGET = Ticket ('этот постбек обрабатывается в нормальном конвеере ASP.NET)
10: Try
11: If Request.Form("__EVENTTARGET") = "undefined" Then
12: Dim db1 As New FlySeasonDataContext
13: Dim ZakazNumber = (db1.AddZakaz(Request.Form("hName"), Request.Form("hTel"), Request.Form("hMail"), Request.Form("hKol"), Request.Form("hPrice"), Request.Form("hID"), Request.Form("hPromo"))).ToList
14: If ZakazNumber IsNot Nothing Then
15: If ZakazNumber.Count > 0 Then
16: ZakazNum.Value = ZakazNumber(0).Zakaz_ID
17: End If
18: End If
19: End If
20: Catch ex As Exception
21: '
22: End Try
23: End If
24: End Sub
3.Сборка jQuery UI.
Jquery UI - весьма тяжеловесная игрушка (на мой взгляд совершенно непригодная для массовых и нагруженных сайтов). Мало того, что нужна сама jQuery - которая является адской и тормозной надстройкой над JavaScript - нужна еще и библиотека jQuery UI.
Для того, чтобы хоть как-то подсластить эту пилюлю - создан конфигуратор jQuery UI, которым вы можете создать для своего сайта не полный, а усеченный вариант jQuery UI. Разница весьма существенная скажем jQuery UI 1.8.16 - только Core - весит всего 4 КБ (и 89 КБ сама jQuery), а все 31 функции jQuery UI 1.8.16 весят 205 КБ!
То есть даже пустая страничка сайта будет весить треть мегабайта! А насколько странички такого сайта будут тормозными! Даже не в плане просто вытаскивания трети мегабайта из web-сервера, а в плане медленного-медленного просчета JavaScript в один поток браузером клиентского кампутера. Собстенно вся идея серверного программирования была придумана чтобы избавится от медленного-медленного просчета JavaScript на каком-нибудь тормозном НетБуке, подключенном по WiFi - где только сама эта библиотека будет вытаскиваться из инета по несколько секунд на каждом постбеке - потом зависание браузера на полминуты - и страничка готова! Помимо того, исполнение кода JavaScript вообще надо специально разрешит браузеру. Поэтому подобный активный функционал следует использовать весьма и весьма осторожно.
|