
Пример работы CascadingDropDown
Контрол CascadingDropDown, входящий в состав ASP.NET Ajax Control Toolkit, служит для создания нескольких зависимых друг от друга выпадающих списков (DropDownList-ов) — например, в первом выбирается страна, во втором после выбора страны загружается список городов выбранной страны и так далее. Очень удобный контрол, позволяет работать как с данными из БД, так и с данными из xml-файлов.
И вот буквально сегодня я столкнулся с одной проблемой. Рассказываю, в чём она заключалась и как я её решил.
В чём проблема?
В одном из своих проектов я использую user-friendly URL's. Для этого я подключаю к проекту
UrlRewriting.NET — компонент для rewrite адресов в ASP.NET-сайте. Кстати, весьма удобная вещь, рекомендую к использованию.
Так вот, страница регистрации физически находится по адресу вроде /system/signup.aspx, но все ссылки на сайте ведут на /signup/ — для большей красоты :) И вот на этой странице перестал работать CascadingDropDown. Выдаёт ошибку «Error method 500» — и всё.
Как я искал проблему
Покопался я немного в коде и после нескольких экспериментов установил, что если страница после реврайта (скрытой переадресации) находится по адресу /signup.aspx или /signup/default.aspx, то CascadingDropDown работает. Если по адресу вроде /signup/a — тоже работает. То есть если страница находится не по адресу /signup/, а заканчивается на имя какого-то файла, то CascadingDropDown работает.
Тогда я додумался поглядеть уже сгенерированный код html-страницы — и быстро понял, в чем проблема. Посмотрите, вот это ASP.NET код на странице signup.aspx:
<asp:DropDownList ID="CountryLives" runat="server" Width="252px" CssClass="inputfield"></asp:DropDownList><br />
<cc1:CascadingDropDown ID="CascadingDropDown2" runat="server"
TargetControlID="CountryLives"
Category="Live"
PromptText="[выберите область]"
LoadingText="[загрузка списка областей]"
ServicePath="~/WebServices/GetLocation.asmx"
ServiceMethod="GetLives"
ParentControlID="Country">
</cc1:CascadingDropDown>
А вот что генерируется в конце концов в html-коде (при загрузке страницы по адресу /signup/):
Sys.Application.add_init(function() {
$create(AjaxControlToolkit.CascadingDropDownBehavior, {"Category": "Live", "ClientStateFieldID": "ctl00_ContentPage_CascadingDropDown2_ClientState", "LoadingText": "[загрузка списка областей]", "ParentControlID": "ctl00_ContentPage_Country", "PromptText": "[выберите область]", "ServiceMethod": "GetLives", "ServicePath": "WebServices/GetLocation.asmx", "id": "ctl00_ContentPage_CascadingDropDown2"}, null, null, $get("ctl00_ContentPage_CountryLives"));
});
Как видно, я ASP.NET-контроле я прописываю ServicePath от корня приложения: ~/WebServices/GetLocation.asmx. В JavaScript-е в генерируемой html-страницеServicePath прописывается как WebServices/GetLocation.asmx – то есть не от корня сайта, а от текущего каталога. То есть определяется неправильно.
Если же адрес страницы заканчивается каким-то файлом (/signup/default.aspx – к примеру), то в JavaScript-е ServicePath имеет значение "../WebServices/GetLocation.asmx". То есть в этом случае определяется правильно.
Итак, как решить проблему?
Теперь, используя результат этих наблюдений, это совсем несложно: просто в ASP.NET коде устанавливаем значение ServicePath не “~/WebServices/GetLocation.asmx”, а “/WebServices/GetLocation.asmx” – принудительно от корня приложения. В таком случае в JavaScript значение параметра ServicePath тоже прописывается как “/WebServices/GetLocation.asmx” – и CascadingDropDown работает так, как и должен был работать. Ура :)