Опубликован: 04.05.2010 | Доступ: свободный | Студентов: 4032 / 454 | Оценка: 4.64 / 4.44 | Длительность: 41:24:00
Практическая работа 4:

Добавление динамических компонент в Интернет-магазин

< Лекция 11 || Практическая работа 4: 1234 || Лекция 12 >

15.3.2. Вызов веб-службы из клиенсткого сценария

Теперь, когда веб-сервис реализован, можно переделать страницу products/default.aspx, сделав так, чтобы она отображала данные, которые будет возвращать веб-сервис. Чтобы облегчить работу с отображением данных, воспользуемся библиотекой ExtJS, ознакомиться и скачать которую можно по адресу: http://www.extjs.com/

Примечание: библиотека ExtJS является коммерческим продуктом. Узнать о правилах лицензирования можно по адресу: http://www.extjs.com/products/license.php

Подключим библиотеку ExtJS к нашему проекту, добавив ссылки на файлы ext-all.css, ext-base.js и ext-all.js в head' е мастера страниц так, как это указано в следующем коде:

<head runat="server">
  <meta content="text/html; charset=utf-8" http-equiv="Content-Type" />
  <title></title>
  <link href="styles/style2.css" media="screen" rel="stylesheet" title="CSS" type="text/css" />
  <link rel='stylesheet' type='text/css' href='styles/ext-all.css' />

  <script type="text/javascript" src="../JS/ext-base.js" ></script>

  <script type="text/javascript" src="../JS/ext-all.js" ></script>

  <script type="text/javascript" src="JS/jquery-1_2_6_min.js"></script>

</head>

Теперь перейдем к странице products/default.aspx, удалим код, содержащийся в ContentPlaceHolder' ах и серверный код.

Теперь в левый ContentPlaceHolder добавим тег script, где разместим следующий код:

Ext.onReady(function(){
   var query = getQueryVariable();
   var result = WebProductService.GetProducts
    (query.category ? query.category : '', 
     query.subCategory ? query.subCategory : '',0, SucceededCallback, FailedCallback);
  });
  function FailedCallback(error) {
    alert(error.message);
  }
  function SucceededCallback(result, eventArgs) {
  }
  function getQueryVariable() {
    //полачаем строку запроса (?a=123&b=qwe) и удаляем знак ?
    var query = window.location.search.substring(1);
    //получаем массив значений из строки запроса вида vars[0] = 'a=123';
    var vars = query.split("&");
    var arr = new Array();
    //переводим массив vars в обычный ассоциативный массив 
    for (var i = 0; i < vars.length; i++) {
      var pair = vars[i].split("=");
      arr[pair[0]] = pair[1];
    }
    return arr;
}

Функцию SucceededCallback на данный момент определять не будем, так как прежде нам потребуется написать вспомогательный код.

В приведенном коде определены 4 функции. Первая функция является анонимной, и выполняется в тот момент, когда библиотека ExtJS закончит инициализацию, что произойдет после полной отрисовки страницы. Вначале будет вызвана вспомогательная функция getQueryVariable, которая позволит определить, какие переменные определены в строке запроса.

Функция getQueryVariable обращается к объекту window.location, который содержит всю строку запроса, после чего обращается в свойству search, содержащему строку параметров в формате ?a=123&b=qwe. Далее у этой строки удаляется первый символ, и она методом split разделяется на множество строк, разделенных знаком &. После этого, каждая полученная строка также разделяется на две, содержащие имя параметра и его значение, и записываются в массив arr, который в данном случае выступает в качестве хэш-таблицы.

Результат вызова функции getQueryVariable помещается в переменную query.

Далее вызывается функция WebProductService.GetProducts, которая представляет собой сгенерированную ScriptManager' ом прокси-функцию, вызывающую одноименный веб-метод. При этом JavaScript устроен так, что вызов самого веб-метода будет отложен до тех пор, пока текущий поток не закончит свою работу. Чтобы узнать, как отработал веб-метод, в функцию WebProductService.GetProducts помимо трех параметров, необходимых для работы, передаются также два метода обратного вызова. Метод FailedCallback будет вызван в случае, если во время выполнения веб-метода произойдет ошибка, и отобразит текст этой ошибки пользователю. Метод SucceededCallback выполнится в случае, если веб-метод успешно завершит свое выполнение. В этом случае переменная result будет содержать результат работы веб-метода.

Уже сейчас можно запустить наш сайт и, если открыть IE Developer Tools и поставить breakpoint в метод SucceededCallback, можно убедиться, что веб-сервис возвращает нужный ответ, изучив переменную result (рис. 15.5).

Переменная result в IE Developer Tools

увеличить изображение
Рис. 15.5. Переменная result в IE Developer Tools

Однако данные будут бесполезны, если пользователь их не увидит. Для того чтобы отобразить данные, воспользуемся клиентским компонентом Ext.grid.GridPanel, который предоставляет во многом схожую функциональность с той, что дает ASP.NET -компонент GridView, но позволяет выполнять большинство действий без взаимодействия с сервером, не перерисовывая страницу.

Как и для ASP.NET GridView необходимо для Ext.grid.GridPanel указать источник данных, который должен наследоваться от класса Ext.data.Store. Так как разработанный веб-метод возвращает данные в формате JSON, то нам подойдет класс Ext.data.JsonStore. Каждый источник данных в этом подходе состоит из двух важных компонент: DataProxy, который отвечает за извлечение данных, и Reader' а, который умеет полученные при помощи DataProxy данные разбирать. И если стандартный класс JsonReader нас устроит для разбора данный, то ни одна из реализаций DataProxy не позволяет обращаться к веб-сервисам. Поэтому мы разработаем собственный (сильно упрощенный) прокси-класс, который назовем Ext.data.FunctionProxy:

Ext.data.FunctionProxy = function(f) {
   Ext.data.FunctionProxy.superclass.constructor.call(this);
   this.func = f;
};

Ext.extend(Ext.data.FunctionProxy, Ext.data.DataProxy, {
   load: function(params, reader, loadCallback, scope, arg) {
   var proxy = this;
   this.func({
      first: params.start,
      count: params.limit,
      sortAsc: params.dir != "DESC",
      scope: params.scope,
      totalCount: proxy.totalCount || 0,
      fields: params.fields
   },
   function(data) {
      var records = reader.readRecords(data);
      proxy.totalCount = records.totalRecords;
      proxy.lastArguments = proxy.arguments;
      loadCallback.call(scope, records, arg, true);
   });
 },
  reload: function() {
   this.load.apply(this, this.lastArguments);
  }
})

При разработке этого класса используется объектно-ориентированный подход программирования на JavaScript, предлагаемый библиотекой ExtJS. Вначале объявляется конструктор класса Ext.data.FunctionProxy, который принимает на вход переменную-функцию f и который вызывает конструктор базового класса. При этом переменная f сохраняется в поле func текущего объекта.

Далее определяется, что класс Ext.data.FunctionProxy является расширением класса Ext.data.DataProxy, у которого переопределены методы load и reload. Это делается при помощи статического метода Ext.extend.

Примечание: использование методов Ext в данном случае позволяет создать подход к программированию, соответствующий традициям ООП. Те же действия можно выполнить, не привлекая объектно-ориентированный код стандартными возможностями JavaScript, однако в этом случае сильно упадет наглядность кода.

Функция load вызывает функцию func, передавая параметры в виде объекта arg, поля которого содежат информацию о номере первой извлекаемой запись, количестве записей, направлении сортировки, области видимости, полном количестве записей и наборе извлекаемых полей. Второй передаваемый в функцию func параметр представляет собой функцию обратного вызова, которая сработает, когда данные с сервера будут извлечены (т.е. заменит функцию SucceededCallback ). Эта функция применяет JsonReader для разбора записей и представления их в виде, понятном различным компонентам, включая Ext.grid.GridPanel, а также сохраняет информацию об общем количестве записей и аргументах вызова. Далее вызывается функция loadCallback, которая и сообщит связанным с этим источником данных компонентам, что в него загружены новые данные.

Функция reload просто повторно вызывает функцию load с последними использованными параметрами.

Последнее, что осталось подготовить – это место, где будет отрисовываться таблица. Для этого добавим ASP-панель на страницу products/default.aspx:

<asp:Panel ID="PanelGrig" runat="server" Style="width:100%;" >

</asp:Panel>

Теперь приступим к реализации функции SucceededCallback. Для простоты, разобьем ее код на несколько частей:

var storeProducts = new Ext.data.JsonStore({
    root: 'Result', remoteSort: true,
    fields: ['ProductID', 'ProductNumber', 'Name', 
             'Color', 'ListPrice', 'FullSize', 'Weight'],
    totalProperty: 'TotalCount',
    proxy: new Ext.data.FunctionProxy(function(arg, loadCallback)
    {
      var query = getQueryVariable();
      var query = getQueryVariable();
      WebProductService.GetProducts(query.category ? query.category : '', 
                                    query.subCategory ? query.subCategory : '', 
                                    arg.first, loadCallback);
    })
});
storeProducts.load({
   params: {
      start: 0,
      limit: 10
   }
});

Здесь мы определяем источник данных storeProducts типа Ext.data.JsonStore, который будем использовать в дальнейшем. При этом указывается, что записи будут храниться в поле Result, а общее количество записей в поле TotalCount возвращаемого веб-сервисом JSON-объекта. Указывается, что сортировка записей проводится на сервере, определяется набор полей, которые будут храниться в источнике данных, и указывается прокси-класс. Здесь мы воспользуемся новым классом Ext.data.FunctionProxy, а передаваемая в него функция, повторяет код анонимной функции, рассмотренной ранее. После этого вызывается функция load источника данных, хотя это не обязательно.

Далее определяется div, в который будет отрисована таблица. Здесь стоит обратить внимание, на то, что в качестве идентификатор при поиске элемента на странице используется не ID ASP-панели, а ее ClientId, которое генерируется ASP.NET автоматически. Данный подход, когда идентификатор "зашит" в код не очень хорош, так как идентификатор может поменяться, если будет изменена сама страница ASP. Но так как у нас всего один ASP-компонент на странице, то этим можно пренебречь:

var el = 
document.getElementById('ctl00_column_l_placeholder_PanelGrig');
Теперь осталось отобразить саму таблицу:
            var grid = new Ext.grid.GridPanel({
                renderTo: el,
                stripeRows: true,
                store: storeProducts,
                colModel: new Ext.grid.ColumnModel({
                    defaults: {
                        width: 120,
                        sortable: true
                    },
                    columns: [
                    { header: 'Номер продукта', dataIndex: 'ProductNumber' },
                    { header: 'Название', dataIndex: 'Name' },
                    { header: 'Цвет', dataIndex: 'Color' },
                    { header: 'Цена', dataIndex: 'ListPrice' },
                    { header: 'Размер', dataIndex: 'FullSize' },
                    { header: 'Вес', dataIndex: 'Weight' }
            
                ]
                }),
                viewConfig: {
                    forceFit: true
                },
                sm: new Ext.grid.RowSelectionModel({ singleSelect: true }),
                width: 600,
                height: 300,
                frame: true,
         bbar: new Ext.PagingToolbar(
            {
                pageSize: 10,
                store: storeProducts,
                listeners:
                {
                    beforechange: function(sender, e) {
                    sender.store.reload(
                            {
                                params:
                                {
                                    start: e.start,
                                    limit: e.limit,
                                    scope: this
                                }
                            });
                        return false;
                    }
                }
            })
            });
        }
< Лекция 11 || Практическая работа 4: 1234 || Лекция 12 >