Ajax Master-detail

При разработке веб-приложений, отображение информации вида master-detail становится зачастую головной болью. Это особенно касается тех случаев, когда на странице со сложной структурой, отображающей достаточно много "тяжелой" информации (возможно, "поднятой" из многих источников) требуется, чтобы при выборе значения из ниспадающего списка, другой элемент управления среагировал каким-то локальным образом. Например, Label отобразил бы детальное описание выбранного элемента. Или же другой список перезаполнился новыми значениями. Отсылка всей страницы обратно на сервер - это сложный и логически-неверный в данном случае процесс, отнимающий как трафик (считаем размер самой страницы и зачастую монструозный по объемам ViewState), так и увеличивающий нагрузку на сервер (вспомним весь жизенный цикл страницы, инициализацию всех объектов и т.д.).


В данном случае, несомненно, применение асинхронной отправки данных на сервер является наиболее эффективым средством. Наиболее простые примеры отыскать в сети не составляет труда, поэтому я решил рассмотреть немного более сложный и жизненный пример, а именно применение master-detail в стандартном серверном элементе управления ASP.NET DataGrid.
Задача следующая: отобразить таблицу устройств, с возможностью ввода и редактирования записей.



В таблице среди прочих столбцов отображаются:

* название устройства
* модель
* офис/комната

наиболее интересным для нас является именно последний из описываемых столбцов, так как в нём при редактировании записи пользователь выбирает офис из списка имеющихся, после этого перезаполняется нижерасположенный список доступных комнат в данном офисе, где может располагаться редактируемое устройство.



программно это выглядит следующим образом:
на клиенте список офисов представлен так

<asp:DropDownList id=ddlstOffice runat="server" Width="100%" DataMember="Offices" DataSource="<%# dsMain %>" DataTextField="ShortDescr" DataValueField="G_ID" onChange="changeRooms(this.name, this.value);" SelectedValue='<%# DataBinder.Eval(Container, "DataItem.OfficeID") %>'></asp:DropDownList>

где важным является onChange="changeRooms(this.name, this.value);" что при смене вызывает клиентский, зарегистрированный через RegisterClientScriptBlock скрипт со следующим содержанием:

function changeRooms(officeControlName, officeID){
            
AdminEditor.EqEditor.GetRooms(officeControlName, officeID, changeRooms_callback);
}

Единственным назначением данной функции является вызов серверного метода GetRooms класса EqEditor и, соответственно, указание клиентской функции обратного вызова changeRooms_callback.
Серверный метод выглядит следующим образом:

[AjaxPro.AjaxMethod]
        public
DdListStruct GetRooms(string controlID, int officeID)
        {
            
string[] arrstr = controlID.Split(':');
            
ListItemCollection lic = new ListItemCollection();
            if(
Session["Rooms"] != null)
            {
               
DataSet ds = new DataSet();
               
ds = (DataSet)Session["Rooms"];               
               
                foreach(
DataRow dr in ds.Tables["Rooms"].Select("PrimeValue=0 OR OfficeID=" + officeID))
                {
                   
ListItem li = new ListItem(dr["ShortDescr"].ToString(), dr["G_ID"].ToString());
                   
lic.Add(li);

                }
               
            }
            
DdListStruct ddlstr = new DdListStruct(lic, String.Format("{0}_{1}_ddlstRoom",arrstr[0], arrstr[1]));
            return
ddlstr;
       }
        public
struct DdListStruct
       
{
            public
DdListStruct(ListItemCollection lic, string controlID)
            {
               
ControlID = controlID;
               
Lic = lic;
            }
            public
string ControlID;
            public
ListItemCollection Lic;
        }

В данном отрывке кода интересными являются следующие детали:
функция возвращает структуру DdListStruct, которая успешно сериализуется компонентой AJAX.Pro и отправляется клиенту. Далее, на клиенте, обращение к полученным данным идет как структурированному объеку (см. код ниже). В большинстве других компонент, да и в том же Atlas'е при пересылке данных на клиент, программисту приходится самому заботиться о том, как в серверном коде упаковать данные в строку а затем на клиенте разбирать, т.е. парсить данную строку, получая данные для дальнейшей обработки. Код при этом может получиться довольно громоздким. Да и зачем программисту загружать себя такой неблагодарной работой, лучше потратить время на более полезные вещи. Структура DdListStruct содержит коллекцию элементов (список комнат), которыми будет заполнен элемент управления и собственно, клиентское имя этого элемента управления. Вот тут мы в плотную подошли к тому, зачем на сервер кроме id офиса для которого возвращаем список комнат, еще и передается controlID, т.е. id управляющего элемента. Ответ довольно прост. Для того, чтобы вычислить клиентское имя detail-элемента управления, соответствующего именно управляющему списку, ведь на странице списков может быть много. В приведенном примере это сделано для наиболее распространенного случая, но все же не универсально. Да и такой цели перед собой не ставил, главное это сам подход.

Клиентская функция обратного вызова выглядит следующим образом:

function changeRooms_callback(res){
    var
_struct = res.value;
    var
select_box = document.getElementById(_struct.ControlID);
    if(
select_box != null) {
       
select_box.options.length = 0;
        var
sel = false;
        for (var
i = 0; i < _struct.Lic.length; i++) {
            if(
i==0) sel=true; else sel=false;
            
select_box.options[i] = new Option(_struct.Lic[i].Text, _struct.Lic[i].Value, sel)
        }
    }
}

В данном коде в объектной модели страницы находится список комнат, соответствующий именно тому списку офисов, который пользователь решил отредактировать в данный момент. Далее список очищается и перезаполняется полученными с сервера значениями. Вот и вся механика. Осталось разобраться в еще одном вопросе. Как AJAX master-detail в DataGrid уживается с жизненным циклом страницы. Об этом далее.

Автор: нет данных