В данной статье я расскажу о своём опыте реализации всплывающего модального окна с некой формой и submit-кнопкой на ASP.NET + jQuery и с теми ошибками, с которыми я столкнулся.
На работе перед началом создания нового проекта (веб сайт) я решил попробовать перейти с клиентской части библиотеки Microsoft AJAX на jQuery. Оснований для этого было несколько, но не об этом сейчас речь.
Дошло дело до всплывающих модальных окон и надо было найти соответствующий плагин. Сначала я попробовал jqModal, но наткнулся на баг в IE7 (мой bug report). После чего я решил использовать jQuery UI dialog, т.к. мне понравилась сама библиотека jQuery UI. Забегая вперёд отмечу, что ThickBox имеет аналогичную проблему той, которую я сейчас опишу.
На тестовом сайте я решил реализовать всплывающее модальное окно для входа пользователя на сайт. Т.е. при клике на линк «Вход» должно показаться всплывающее окошко (модальное) с двумя текстовыми полями (email (как login)/пароль) и двумя кнопками (ок/отмена).
Собственно, создал контрол, который должен был обеспечивать появление этой формы при клике на любые ссылки с классом «lbPopupLink»:
(не забывайте, что тут я javascript код прописываю прямо на странице, что, в принципе, не есть хорошо, т.к. если я контрол поставлю на странице дважды, то получиться дублирующий javascript код — в этом случае лучше использовать ClientScriptManager.RegisterClientScriptBlock; но т.к. данный контрол я точно собирался использовать только один раз на страницу и в целях упрощения статьи я оставлю всё как есть)
Всё заработало, но не до конца: при клике на ссылку с классом «lbPopupLink» действительно появлялось окошко, но при клике на кнопку «Ok» ничего не происходило. Разбираясь и переписываясь с разработчиками jQuery UI я понял в чём дело: чтобы побороть глюки с z-index-ом и stacking-ом в IE при отображении диалога jQuery UI dialog при показе окошка перемещает его внутри DOM-а в самый конец страницы, располагая его прямо в конце <body>. Обычно на других платформах этого не замечали, т.к. просто для каждой такой «всплывающей» формы делали обрамляющий <form> с нужным action и всё работало. Но в идеалогии ASP.NET <form> элемент должен быть только один. Получалось, что наша форма с <input type=«submit» value=«Ок»… /> переносилась за пределы одной единственной формы и потому клик на кнопку «Ок» ничего не делал. Об этом можно почитать в моей переписке с разработчиками. В этой переписке они (разработчики) сами предложили несколько временных решений и я реализовал одно из них: при нажатии на «Ок» диалог перед уничтожением клонируется и вставляется обратно в форму ASP.NET (id=«aspnetForm»). Ради чего была написана маленькая функция. Там ещё был один «прикол» с IE: оказалось, что в IE при использовании jQuery.clone не копируются значения <input type=«password»… /> полей, потому пришлось их «ручками» перегонять. Собственно, сама функция вот (не пинайте — на jQuery работаю отсилы неделю):
И вот чем нужно дополнить наш javascript блок из начала статьи, чтобы использовать эту функцию:
Вот, что получилось:
Исходный код всего тестового проекта.
PS: блин, для первого топика на хабре хотел покороче :|
На работе перед началом создания нового проекта (веб сайт) я решил попробовать перейти с клиентской части библиотеки Microsoft AJAX на jQuery. Оснований для этого было несколько, но не об этом сейчас речь.
Дошло дело до всплывающих модальных окон и надо было найти соответствующий плагин. Сначала я попробовал jqModal, но наткнулся на баг в IE7 (мой bug report). После чего я решил использовать jQuery UI dialog, т.к. мне понравилась сама библиотека jQuery UI. Забегая вперёд отмечу, что ThickBox имеет аналогичную проблему той, которую я сейчас опишу.
На тестовом сайте я решил реализовать всплывающее модальное окно для входа пользователя на сайт. Т.е. при клике на линк «Вход» должно показаться всплывающее окошко (модальное) с двумя текстовыми полями (email (как login)/пароль) и двумя кнопками (ок/отмена).
Собственно, создал контрол, который должен был обеспечивать появление этой формы при клике на любые ссылки с классом «lbPopupLink»:
<%@ Control Language=«C#» AutoEventWireup=«true» Inherits=«Website.Controls.LoginBox» CodeBehind=«LoginBox.ascx.cs» %>
<asp:Panel runat=«server» ID=«panelMain» ToolTip=«Введите ваш email и пароль для входа на сайт»>
<asp:Label runat=«server» ID=«Label1» AssociatedControlID=«txtEmail» CssClass=«req» Text=«Ваш email»></asp:Label>:
<br />
<asp:TextBox runat=«server» ID=«txtEmail» AutoCompleteType=«Email» MaxLength=«255»></asp:TextBox>
<asp:RequiredFieldValidator runat=«server» ID=«RequiredFieldValidator1» ControlToValidate=«txtEmail» Display=«Dynamic» ErrorMessage=«Необходимо ввести email.»></asp:RequiredFieldValidator>
<br />
<br />
<asp:Label runat=«server» ID=«Label2» AssociatedControlID=«txtPassword» CssClass=«req» Text=«Пароль»></asp:Label>:
<br />
<asp:TextBox runat=«server» ID=«txtPassword» TextMode=«Password» MaxLength=«255»></asp:TextBox>
<asp:RequiredFieldValidator runat=«server» ID=«RequiredFieldValidator2» ControlToValidate=«txtPassword» Display=«Dynamic» ErrorMessage=«Необходимо ввести пароль.»></asp:RequiredFieldValidator>
<br />
<br />
<asp:CheckBox ID=«chkRememberMe» runat=«server» Text=«запомнить меня.» />
<br />
<br />
<asp:Literal ID=«strError» runat=«server» EnableViewState=«False»></asp:Literal>
<br />
<asp:Button ID=«butOk» runat=«server» Text=«Войти» OnClick=«butOk_Click» />
<asp:Button ID=«butCancel» runat=«server» Text=«Отмена» CausesValidation=«false» />
</asp:Panel>
<script type=«text/javascript»>
$(document).ready(function()
{
$("#<%= this.panelMain.ClientID %>").css(«display», «block»);
$("#<%= this.panelMain.ClientID %>").dialog
({
autoOpen: false,
modal: true,
width: 400,
height: 300,
dialogClass: «popupDialog»,
resizable: false,
overlay: { opacity: 0.5, background: «black» }
});
$(".lbPopupLink").click(function()
{
$("#<%= this.panelMain.ClientID %>").dialog(«open»);
$("#<%= this.txtEmail.ClientID %>").focus();
return false;
});
$("#<%= this.butCancel.ClientID %>").click(function()
{
$("#<%= this.panelMain.ClientID %>").dialog(«close»);
return false;
});
});
</script>
* This source code was highlighted with Source Code Highlighter.
(не забывайте, что тут я javascript код прописываю прямо на странице, что, в принципе, не есть хорошо, т.к. если я контрол поставлю на странице дважды, то получиться дублирующий javascript код — в этом случае лучше использовать ClientScriptManager.RegisterClientScriptBlock; но т.к. данный контрол я точно собирался использовать только один раз на страницу и в целях упрощения статьи я оставлю всё как есть)
Всё заработало, но не до конца: при клике на ссылку с классом «lbPopupLink» действительно появлялось окошко, но при клике на кнопку «Ok» ничего не происходило. Разбираясь и переписываясь с разработчиками jQuery UI я понял в чём дело: чтобы побороть глюки с z-index-ом и stacking-ом в IE при отображении диалога jQuery UI dialog при показе окошка перемещает его внутри DOM-а в самый конец страницы, располагая его прямо в конце <body>. Обычно на других платформах этого не замечали, т.к. просто для каждой такой «всплывающей» формы делали обрамляющий <form> с нужным action и всё работало. Но в идеалогии ASP.NET <form> элемент должен быть только один. Получалось, что наша форма с <input type=«submit» value=«Ок»… /> переносилась за пределы одной единственной формы и потому клик на кнопку «Ок» ничего не делал. Об этом можно почитать в моей переписке с разработчиками. В этой переписке они (разработчики) сами предложили несколько временных решений и я реализовал одно из них: при нажатии на «Ок» диалог перед уничтожением клонируется и вставляется обратно в форму ASP.NET (id=«aspnetForm»). Ради чего была написана маленькая функция. Там ещё был один «прикол» с IE: оказалось, что в IE при использовании jQuery.clone не копируются значения <input type=«password»… /> полей, потому пришлось их «ручками» перегонять. Собственно, сама функция вот (не пинайте — на jQuery работаю отсилы неделю):
// Global variables
var __passwordCloneValues;
//
// Function that will close jQuery UI dialog, clone it back to aspnet form and perform submit.
// Should be applied to the dialog.
//
// Arguments:
// butOkId — id of the submit button
//
$.fn.extend({
dialogCloseAndSubmit: function(butOkId)
{
if (!Page_IsValid)
return false;
__passwordCloneValues = new Array();
$(":password", $(this)).each(function()
{
__passwordCloneValues.push($(this).val());
});
__passwordCloneValues = __passwordCloneValues.reverse();
var dlg = $(this).clone();
$(this).dialog(«destroy»).remove();
dlg.css(«display», «none»);
$(«form:first»).append(dlg);
$(":password", dlg).each(function()
{
$(this).val(__passwordCloneValues.pop());
});
$("#" + butOkId, dlg).click();
return true;
}
});
* This source code was highlighted with Source Code Highlighter.
И вот чем нужно дополнить наш javascript блок из начала статьи, чтобы использовать эту функцию:
$("#<%= this.butOk.ClientID %>").click(function()
{
return $("#<%= this.panelMain.ClientID %>").dialogCloseAndSubmit($(this).attr(«id»));
});
* This source code was highlighted with Source Code Highlighter.
Вот, что получилось:
Исходный код всего тестового проекта.
PS: блин, для первого топика на хабре хотел покороче :|