Показаны сообщения с ярлыком ExtJS 3. Показать все сообщения
Показаны сообщения с ярлыком ExtJS 3. Показать все сообщения

среда, 14 сентября 2011 г.

Использование Ext JS 4 виджетов в рамках Ext JS 3 интерфейса

Ext JS 4 представляет разработчикам много новых возможностей, в числе которых, например, большое количество новых графиков и диаграмм. Сравните - для четвертой версии приведено 22 примера использования элементов Chart против всего четырех примеров для третьей версии. Внушающе, не правда ли?

Однако что, если в наличии есть большое приложение, реализованное на третьей версии Ext JS и возникает необходимость использования некоторых виджетов четвертой версии? Например, типична ситуация, когда в приложение нужно добавить новые диаграммы, но переписывать всё на "четверку" нецелесообразно.

понедельник, 15 августа 2011 г.

ExtJS: разработка интерфейса независимо от источников данных. Кросс-доменный AJAX для загрузки хранилищ

Удаленные источники для хранилищ ExtJS - это источники, расположенные в другом домене или, как их еще называют, кросс-доменные источники. Вероятно, вы знаете о том, что традиционные XMLHttpRequest запросы (известные в народе как AJAX запросы) имеют ряд ограничений, связанных в первую очередь с безопасностью - в частности, XMLHttpRequest запросы могут быть посланы только к адресу с одноименным протоколом, доменом и портом по отношению к запрашивающей странице. Таким образом, нельзя сделать традиционный AJAX запрос с сайта site1.com к сайту site2.com.

Тем не менее, такая необходимость часто возникает, в таких случаях используется так называемый cross-domain  (кросс-доменный) AJAX. Стоит отметить, что непосредственно к AJAX эти технологии уже не относятся, название в данном случае - не более, чем дань традиции. О методах, используемых в кросс-доменных запросах, можно почитать, например, в статье на javascript.ru.

Одной из проблем разработки интерфейсов на ExtJS является зависимость от динамически загружаемых данных: фактически, большинство компонентов фреймворка используют те или иные данные, получаемые при помощи AJAX запросов. Например, так выглядит традиционное описание хранилища:

myStore = new Ext.data.JsonStore({
    url:    'getUsers',
    method: 'POST',
    root: 'items',
    fields: [{name:"id"},{name:"name"}]
});

URL, указанный первым параметром, ссылается на страницу того же домена, в котором должна находиться разрабатываемая страница, иначе AJAX запрос на загрузку данных не сможет быть произведен. Таким образом, разрабатываемая html страница и javascript код в ее составе должны быть частью домена-источника данных. Это не всегда удобно и не всегда возможно.

Использование кросс-доменных запросов в таких случаях - отличное решение. Посмотрите, как просто можно заставить работать ExtJS приложение с данными любого домена:

myStore = new Ext.data.JsonStore({
    proxy: new Ext.data.ScriptTagProxy({
            url:    'http://vasilij.com/getUsers',
    }),
    method: 'POST',
    root: 'items',
    fields: [{name:"id"},{name:"name"}]
});

В код сервлета необходимо добавить отслеживание GET параметра callback. Дело в том, что при использовании ScriptTagProxy, ExtJS добавляет в строку запроса параметр callback, таким образом, что в нашем случае запрос будет иметь вид:

http://vasilij.com/getUsers&callback=stcCallback10

Возвращаемый JSON должен быть обернут в функцию, название которой соответствует переданному параметру callback. В случае сервлета:

String callback = request.getParameter("callback");
if (callback == null){
  response.setContentType("text/javascript");
  out.println(jsonString);
}
else{
  response.setContentType("application/x-json");
  out.println(callback.concat("(").concat(jsonString).concat(")"));
}

Таким образом, при отсутствии callback параметра в строке запроса возвращается обычный JSON, в противном случае JSON оборачивается в функцию.

После внесения в код приложения описанных изменений можно разрабатывать интерфейс абсолютно независимо от всей остальной части приложения; сервлеты-поставщики данных могут находиться в любом домене, и даже простая html страница на рабочем столе с подключенным ExtJS сможет получать данные и корректно их обрабатывать и отображать. После окончания процесса разработки и тестирования можно заменить Ext.data.ScriptTagProxy на Ext.data.HttpProxy для использования AJAX запросов.

Приятного кодинга вам!

пятница, 8 июля 2011 г.

ExtJS labelWidth или задаем ширину пометок к полям формы

В ExtJS метка Label используется как аналог традиционному HTML элементу <label></label>, используемому для определения текстовых названий полей формы. Зачастую возникает необходимость дополнительно стилизовать эти метки, для этого в ExtJS используется параметр labelStyle, которому соответствует строка с CSS описанием. Естественно, таким образом можно задать цвет метки, изменить шрифт, ширину элемента и т.д:

{
  fieldLabel: 'Введите Ваше имя',
  name: 'userName',
  labelStyle: 'color: red; width: 200px; font-weight: bold;'
}

Однако, задав ширину метки в лоб через labelStyle, можно наткнуться на ряд неприятных и не всегда очевидных последствий, например, как показано на скриншоте ниже.



Проблема здесь заключается в том, что позиционированием элементов занимается ExtJS, если же вы меняете размер и расположение через CSS, вы начинаете сами отвечать за эти действия. Логичным выходом из сложившейся ситуации видится использование встроенных механизмов ExtJS для изменения размеров элементов - и это абсолютно верно.

Для изменения ширины меток Label необходимо использовать параметр слоя form, именуемый labelWidth. Да, именно слоя form, а не самого элемента формы field (например, TextField). Ширина всех меток на форме указывается в параметрах формы, как показано ниже:

myform = {
  labelWidth: 140,
  layout: 'form',
  style: 'padding: 15px;',
  defaults: {
    xtype: 'displayfield',
    labelStyle: 'color: #767782; font-weight: bold; width: 320px;'
  },
  items: [
    {
      fieldLabel: 'Ваше имя, сэр!',
      name: 'sirName'
    },
    {
      fieldLabel: 'Ваше пароль, милейший!',
      name: 'sirPassword'
    }
  ]
}

Таким образом мы получим корректное поведение ExtJS и корректное отображение меток формы.

четверг, 9 июня 2011 г.

ExtJS 3 grid colspan или группировка колонок



Задача: необходимо объединить несколько колонок таблицы grid. В чистом HTML для этого существует атрибут colspan, но как реализовать это в элементах ExtJS?
В данной статье рассматривается третья версия ExtJS.

Решение.

Необходимо воспользоваться плагином ColumnHeaderGroup, пример использования которого можно посмотреть здесь.

Итак, для осуществления группировки колонок, необходимо:

  1. подключить js файл плагина (ColumnHeaderGroup.js)
  2. в коде Ext.onReady() описать заголовок таблицы следующим образом:

    // объявляем массив для хранения первой строки заголовка
    var row1 = [];
    // заполняем его
    row1.push({
      header: ''
    });
    row1.push({
      header: 'Ставка, руб./1 GT',
      colspan: '3',   // да-да, это аналог того самого colspan
      align: 'center' // выравнивание текста в ячейке
    });
  3. создать заголовок таблицы, используя плагин:

    groupedRows = new Ext.ux.grid.ColumnHeaderGroup({
      rows: [ row1 ]
    });

    В случае, если таких строк с объединенными колонками будет несколько - просто перечислите их черех запятую в массиве rows:

    groupedRows = new Ext.ux.grid.ColumnHeaderGroup({
      rows: [ row1, row2, row3 ]
    });


  4. подключить инициализированный плагин к таблице grid:

    grid = new Ext.grid.GridPanel({                
      xtype: 'grid',
      store: companiesStore,
      plugins: [ groupedRows ],
      border: false, frame: false,
      columns: columnModel
    });

Таким образом, заголовок таблицы делится на 2 типа строк - самая нижняя строка, описываемая в параметре columns модели GridPanel согласно всем стандартным правилам описания колонок таблицы grid, и все остальные строки, располагающиеся выше и объединяющие колонки при помощи плагина. Закомментируйте строку plugins: [ groupedRows ] и в заголовке останется только одна строка, нижняя, все верхние строки должны описываться как показано выше при инициализации плагина.

Описанный способ позволяет создавать сколь угодно сложные структуры заголовков таблиц.

понедельник, 23 мая 2011 г.

ExtJS 3 суммирование строк таблицы Grid


Рассматриваемая сегодня задача встречается очень часто: необходимо добавить в таблицу Grid дополнительную строку, которая будет выводить сумму значений из расположенных выше строк. В статье рассматривается ExtJS 3, в новой четвертой версии описанный функционал уже включен в ядро и хорошо документирован.


Создано несколько версий плагина GridSummary с различными доработками, они описаны здесь. Примеры использования плагина можно посмотреть в исходном варианте плагина здесь, но в проекте лучше использовать финальную на момент написания статьи версию. Да, для загрузки файлов необходимо зарегистрироваться на официальном форуме разработчкиков ExtJS (http://www.sencha.com/forum/), если вы до сих пор этого не сделали.

Ниже описан процесс подключения и конфигурирования плагина.

Первым шагом является подключение css и js файлов:

<link rel="stylesheet" href="gridsummary.css"/>
<script src='./js/gridsummary.js'></script>

Обратите внимание, что оба файла необходимо подключать после всех файлов самого ExtJS.

Вторым шагом является подключение плагина в теле функции onReady():

var summary = new Ext.ux.grid.GridSummary();
formDataGrid = new Ext.grid.EditorGridPanel({
  plugins: [ summary ],  store: myStore,
  cm: [
    {
      header: 'Компания',
     dataIndex: 'comp',
      summaryRenderer: function(){ return 'Всего: ';}
    }
    {
      header: 'Цена',
      dataIndex: 'price' ,
      summaryType: 'sum'
    },
    {
      header: 'Цифра какая-то',
      dataIndex: 'someInt',
      summaryType: 'avg'
    }
    //...
  ]

Как вы видите, все довольно просто. Вы указываете плагин summary и в описание ColumnModel добавляете описание параметров работы плагина. Основных параметра два - это summaryType, определяющий тип обработки данных (суммирование sum, вычисление среднего avg, подсчет количества строк count и др.) и summaryRenderer - функция-рендер для ячейки строки суммы, в случае указания  она определяет итоговый состав и стиль выводимых данных.

Например, для вывода положительной суммы зеленым цветом, а отрицательной красным, необходимо написать такой простенький рендер:

...
summaryRender: function pctChange(val) {
  if (val > 0) {
    return '<span style="color: green;">' + val + '%</span>';
  } else if(val < 0) {
    return '<span style="color: red;">' + val + '%</span>';
  }
  return val;
}
...

Успехов!

ExtJS найти элемент и изменить его содержимое

Задача: необходимо на лету по ID элемента изменить его содержимое. Например, моей задачей было изменение текста ссылки, позволяющей сворачивать и разворачивать элемент на странице. Когда элемент свернут, текст ссылки должен быть "развернуть", когда развернут - соответственно, "свернуть".

Подобные задачи легко решаются любым современным Javascript фреймворком. В случае ExtJS изменение содержимого элемента с id area будет выглядеть так:

Ext.fly('toggleLink').update('Свернуть описание');

Найти элемент по его классу также несложно, можно воспользоваться полноценным CSS селектором:

var parent = Ext.get("IdOfParent");
var aHrefs = parent.select("div.myCoolDivClass a").elements;

четверг, 19 мая 2011 г.

JavaScript is_int

Простейшая функция, проверяющая, является ли переменная целым числом. Своего рода аналог php is_int().

function is_int(x) { 
   var y = parseInt(x); 
   if (isNaN(y)) return false; 
   return x == y && x.toString() == y.toString(); 
}

В ExtJS есть отличная функция Ext.num(i, 0), проверяющая, является ли i целым числом и возвращающая его в случае успеха или 0 (второй параметр) в случае неудачи. Например:

i = Ext.num(i, 0)

понедельник, 18 апреля 2011 г.

ExtJS проблемы в Internet Explorer

ExtJS, как гласит официальный сайт, является кроссплатформенным фреймворком для построения Rich Internet Application - "богатых интернет приложений", то есть, приложений, обладающих функциональностью обычных настольных программ. И это действительно так.

Однако, нередко при работе с ExtJS некоторые проблемы с кроссбраузерностью все-таки наблюдаются. Тем не менее, стоит отдать должное разработчикам фреймворка, эти проблемы обусловлены в первую очередь разными реализациями Javascript в разных браузерах и для их устранения следует всего лишь следовать хорошему тону программирования.

Нередко наблюдается, когда интерфейс отлично функционирует в Firefox, но даже не отрисовывается в Internet Explorer и браузер выдает многочисленные сообщения об ошибках.
Как правило, причина такого поведения заключается в разной реализации массивов Javascript. Известно, что в ExtJS для конфигурирования элементов используются исключительно объекты и массивы объектов, например:

vp = new Ext.Viewport({
    layout: 'border',
    renderTo: Ext.getBody(),
    items: [
        firstElement,
        secondElement,
        thirdElement
    ]
});

Здесь создается Viewport, элементами которого являются firstElement, secondElement и thirdElement - и этот код будет работать одинаково во всех заявленных разработчиками ExtJS реализациях браузеров. Однако, попробуйте выполнить этот код:

vp = new Ext.Viewport({
    layout: 'border',
    renderTo: Ext.getBody(),
    items: [
        firstElement,
        secondElement,
        thirdElement, // запятая
    ]
});

Internet Explorer выдаст ошибку. Проблема в реализации массивов Javascript: если после последнего элемента стоит запятая, IE считает, что в конце массива присутствует еще один, пустой, элемент. ExtJS пытается обработать этот элемент, но, так как он фактически пуст, генерируется ошибка.

Другие реализации Javascript (в Firefox, например) на запятую после последнего элемента массива внимания не обращают, однако завершать последний элемент запятой считается дурным тоном программирования.

Приведенная ошибка - наиболее часто встречающаяся при разработке на ExtJS, и ее необходимо всегда иметь в виду. Будьте внимательны к последним элементам массивов и объектов, и это упростит вам жизнь при последующем тестировании кроссбраузерности ваших интерфейсов.

Вторая проблема - использование в коде конструкции console.log() для вывода сообщений в консоль Firebug или Chrome. Internet Explorer, к сожалению, не имеет консоли как таковой, поэтому попытки ее использования генерируют ошибку; необходимо помнить, что после окончания тестирования и отладки интерфейса, если вы использовали вывод сообщений в консоль, их необходимо удалить или закомментировать. Впрочем, эта ошибка, в отличие от вышеописанной, легко определяется.

четверг, 14 апреля 2011 г.

Рекомендации начинающим изучение Ext JS

По своему опыту использования Ext JS могу сказать, что в общем и целом этот фреймворк меня приятно удивил и я уверенно могу сказать, что время на его изучение себя оправдывает. Ниже - несколько общих слов о Ext JS и рекомендации решившим начать работу с ним.

Зачем


Ext JS позволяет писать сложные интерфейсы по-настоящему быстро, полностью соответствуя концепции RAD - Rapid Application Development. Не нужно заниматься html и css кодированием для разработки по-настоящему богатых функциональных интерфейсов, выполненных в едином стиле, вместо этого вы пишете несложный javascript код, выполняющий всю работу за вас.

Команда Ext JS создала целую коллекцию примеров (для 3й версии Ext JS) для ознакомления с основными возможностями фреймворка. Посмотрите, какие изящные конструкции вы сможете создавать минимальным количеством JS кода! Абсолютно все конфигурируется под себя, каждый элемент обладает огромным количеством событий (events), позволяющих реагировать на различные действия со стороны пользователя. Разработчикам удалось создать действительно очень и очень гибкий инструмент для построения классых веб интерфейсов.

Возможно, вы уже работали с некоторыми javascript фреймворками, например, JQuery или Prototype и уже используете их в своих проектах. Ext JS спроектирован таким образом, чтобы обеспечить возможность корректной работы и с другими фреймворками, таким образом, вы можете абсолютно спокойно оставить ваши наработки на JQuery и начать внедрять элементы Ext JS. Дело в том, что Ext JS осуществляет работу с DOM (Document Object Model, объектная модель документа) посредством так называемых адаптеров, причем в комплекте поставки уже есть 4 адаптера - родной ext, jquery, prototype и yui. Другими словами, фреймворк может использовать разные библиотеки для выборки элементов страницы, поэтому можно не беспокоться о совместимости и при желании продолжать пользоваться параллельно другим знакомым фреймворком.

С чего начать


Начать, пожалуй, следует с некоторых вводных статей о Ext JS. Для русскоязычного пользователя отличным вступлением может послужить небольшой цикл статей от techwork.ru. Обратите внимание, что в статьях описана третья версия, однако, прочитав этот материал, вы уже будете иметь общее представление о фреймворке и работе с ним.

Дальнейшее изучение Ext JS я бы порекомендовал продолжить с книгой, коих в общем-то достаточно. Да, к сожалению, на русский язык материалы по фреймворку пока не переведены, поэтому для ознакомления потребуется некоторое знание технического английского. Впрочем, спешу успокоить тех, кого это пугает - язык в книгах по Ext JS довольно простой.

Отличная книга - Learning Ext JS 3.2 (вот). В сети есть pdf в отличном качестве, который можно распечатать, почитать на мониторе/iPad'е/электронной книге.

В настоящий момент я пишу книгу Ext JS 4: разработка Rich Internet Applications на русском языке.

Разработчику


Безусловно, одним из наиболее часто используемых источников информации является официальная документация (для третьей версии Ext JS) по API Ext JS. Приведены все конфигурационные опции, в показательных случаях примеры использования - составленная весьма удобным образом, эта документация служит отличным справочным пособием любого Ext JS разработчика.

Выше я упоминал готовые примеры (для третьей версии Ext JS) использования фреймворка - чрезвычайно полезный инструмент, когда нужно быстро посмотреть, как реализуется какой-то функционал. К тому же, читать коды от самих разработчиков очень интересно и познавательно.

Официальные Tutorials (руководства) также являются зачастую хорошим источником для восполнения недостающих знаний.

В общем случае, для начала работы с Ext JS требуется не более 20 минут знакомства с документацией или литературой - потратьте это время и уже скоро сможете оценить всю мощь этого замечательного инструмента. Классных интерфейсов вам!

понедельник, 11 апреля 2011 г.

ExtJS выделить строку в таблице grid

Проблема: выделить одну строку (например, первую) в таблице grid сразу после ее загрузки. Казалось бы, проблемы как таковой здесь нет - получили SelectionModel таблицы и сделали selectRow(rowIndex). По сути - все действительно так, однако необходимо выбрать нужный момент для выполнения этого кода. Попробуйте, например, выделить строку в событии afterrender или render - увы, выделение не сработает. Решение, тем не менее, безусловно есть.

Решение: необходимо воспользоваться событием viewready, возникающим, когда таблица grid полностью отрисована на странице и готова к манипуляциям. Как гласит документация, viewready используется как раз для осуществления выделения строки по умолчанию.

Предположим, что нам нужно выделить первую строку таблицы:

grid = new Ext.grid.EditorGridPanel({
  store: myStore,
  height: 500,
  border: false, frame: false,
  cm: myColumnModel,
  selModel: new Ext.grid.RowSelectionModel({singleSelect:true}),
  listeners: {
    viewready: function(g){
      g.getSelectionModel().selectRow(0);
    }
  }

Обратите внимание, что в случае необходимости использования так называемой модели выделений SelectionModel необходимо явно указывать selModel в конфигурации таблицы, в противном случае вызов метода grid.getSelectionModel() вызовет ошибку.

пятница, 8 апреля 2011 г.

ExtJS убрать обрезку строк в таблице Grid

Таблицы ExtJS (GridPanel и EditorGridPanel) отлично подходят для отображения и удобного редактирования различных типов данных. Однако по умолчанию ячейки таблицы не показывают большие строки полностью, обрезая их до видимой части, как видно на рисунке ниже.

Иногда такое поведение неприемлемо и необходимо предоставить пользователю возможность видеть всю текстовую информацию целиком.
 

Сделать это чрезвычайно просто - необходимо перезаписать CSS параметр .x-grid3-cell-inner для редактирования стиля ячейки и свойства .x-grid3-hd-inner для редактирования стиля заголовка таблицы соответственно. Таким образом, чтобы включить режим просмотра всего текста (wrap) в ячейке, необходимо обозначить следующий стиль:

.x-grid3-cell-inner { white-space: normal; }

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

.x-grid3-hd-inner { white-space: normal; }

Спасибо.

Вообще говоря, подобным образом можно править все стили, используемые в ExtJS интерфейсе - узнать названия стилей и посмотреть их текущие параметры можно в Firebug'е.

ExtJS сделать ячейку/столбец Editor Grid нередактируемой

Проблема: на входе имеем таблицу Editor Grid (EditorGridPanel), в которой необходимо запретить редактирование некоторых ячеек, строк или столбцов. Известно, что по умолчанию все ячейки таблицы EditorGridPanel являются редактируемыми.

Решение. Как всегда, разработчики ExtJS продумали все за нас - необходимо перезаписать конфигурационную функцию isCellEditable модели колонок ColumnModel, определяющую возможность редактирования конкретной ячейки по индексу ее колонки и строки.

var store = new Ext.data.Store({...});
var colModel = new Ext.grid.ColumnModel({
  columns: [...],
  isCellEditable: function(col, row) {
    if (col == 2) return false; else return true;
  }
});
var grid = new Ext.grid.GridPanel({
  store: store,
  colModel: colModel,
  ...
});

В приведенном примере разрешается редактировать всю таблицу, кроме третьей колонки - строки и колонки начинают нумерацию с нуля, поэтому в теле функции isCellEditable проверяется значение col, равное 2.

Неплохо, не так ли? Усложним задачу. Что, если нам нужно решить, стоит ли позволять редактировать ячейку в зависимости от данных в другой колонке? Нет ничего проще!

var store = new Ext.data.Store({...});
var colModel = new Ext.grid.ColumnModel({
  columns: [...],
  isCellEditable: function(col, row) {
    if (col == 2){
      rec = store.getAt(row);
      if (rec.get('notEdit') === true)
        return false;
      else
        return true;
    } else
      return true;
  }
});
var grid = new Ext.grid.GridPanel({
  store: store,
  colModel: colModel,
  ...
});

В данном случае проверяется значение поля notEdit рассматриваемой записи, и если в нем хранится true - ячейка в третьем столбце становится нередактируемой. Остальные столбцы и ячейки при этом продолжают нормально функционировать.

четверг, 7 апреля 2011 г.

ExtJS получить параметры URL/GET параметры

Проблема: необходимо использовать GET параметры, содержащиеся в URL. Например, по каким-то причинам клиенту на JS необходимо получить доступ к параметру id запроса http://site/page.php?id=132.

Решение. Двухстрочная функция, расширяющая возможности ExtJS.

Ext.getUrlParam = function(param) {
    var params = Ext.urlDecode(location.search.substring(1));
    return param ? params[param] : params;
};

Использование:

id = Ext.getUrlParam('id');

вторник, 29 марта 2011 г.

ExtJS: изменились ли записи в хранилище?

Проблема: необходимо узнать, изменились ли записи в хранилище Store . Пример практической задачи, сводящейся к описанной - например, проверка состояния таблицы EditorGridPanel.

Решение:

if (Store.getModifiedRecords().length == 0)
    alert('Записи изменены');
else
    alert('Записи не изменены');

Аналогично можно проверить, например, изменились ли записи EditorGridPanel:

if (EditorGridPanelElement.store.getModifiedRecords().length == 0)
    alert('Записи изменены');
else
    alert('Записи не изменены');

Однако, этот подход не обнаруживает события удаления записей из хранилища! Таким образом, удалив одну или несколько записей, мы изменили хранилище, однако getModifiedRecords() не вернет нам ничего - событие удаления необходимо отслеживать самостоятельно. Предлагаемый автором вариант предельно прост: при загрузке хранилища мы устанавливаем ему метку dirtyMark, равную false - операции удаления не производились; при удалении обновляем метку в true:

  var myStore = new Ext.data.JsonStore({
    url: 'serviceUrl/',
    root: 'result',
    fields: myFields,
    listeners: {
      remove: function(){
        myStore.dirtyMark = true;
      },
      load: function() {
        myStore.dirtyMark = false;
      },
      save: {
        myStore.dirtyMark = false;
      }
    }
  });

Условие проверки изменения состояния хранилища в таком случае будет выглядеть следующим образом:

if (Store.getModifiedRecords().length == 0
       && myStore.dirtyMark === true)
    alert('Записи изменены');
else
    alert('Записи не изменены');

понедельник, 28 марта 2011 г.

ExtJS FormPanel, распределенный по табам TabPanel


Суть: имеем форму FormPanel, распределенную по нескольким табам (вкладкам) TabPanel. В качестве примера можно привести форму 4 на официальной странице примеров ExtJS.

Проблема: при отправке формы отправляются поля не все табов, если быть точным - отправляются только просмотренные табы.

Метод решения: очевидно, необходимо автоматически "просмотреть" или отрендерить (отрисовать) все табы TabPanel.

Решение: добавить в конфигурацию TabPanel параметр deferredRender: false, отвечающий за отложенный рендеринг табов. При установке этого параметра в false все табы рендерятся при первой возможности, не дожидаясь действий со стороны пользователя.

ExtJS ComboBox не отправляет valueField?

При использовании в формах ExtJS выпадающего списка ComboBox вы, должно быть, заметили проблему: форма отправляет отображаемое значение выпадающего списка (displayField), а не его фактическое значение (valueField). Если провести аналогию с обычным <select> HTML - отправляется Вася, а не 3.

<select name="user">
    <option value="1">Саша</option>
    <option value="2">Иннокентий</option>
    <option value="3">Вася</option>
</select>

Чтобы решить данную проблему и заставить ExtJS отправлять 3 вместо Вася, необходимо добавить конфигурационный параметр hiddenName:


var userName = new Ext.form.ComboBox({
    id:'userName',
    name: 'userName',
    fieldLabel: 'Имя',
    store: usersStore,
    displayField: 'name',
    valueField: 'id',
    hiddenName : 'razdel'
});

Будьте внимательны, параметр hiddenName должен быть уникальным для каждого поля формы - он используется при ее отправке вместо параметра name.

Протестировать параметры при отправке формы удобно с помощью Firebug.

воскресенье, 27 марта 2011 г.

RESTful приложение на ExtJS 3



О том, что такое RESTful приложение и почему это круто можно почитать на Wiki, хабре или даже в статье ibm. Фактически, RESTful приложение - это приложение, построенное на вызове удаленных REST веб сервисов, выполняющих CRUD функции (Create, Read, Update, Delete - создать, прочитать, обновить и удалить соответственно). Каждый REST сервис характеризуется URI, по которому он вызывается, и некоторым набором принимаемых и возвращаемых параметров.

Особенность REST подхода состоит в активном использовании HTTP методов для выполнения CRUD функций: вместо использования URI адресов типа

/user/23/
/user/23/?action=delete
/user/23/?action=update


предлагается использовать стандартный адрес /user/23/, обращение к которому ведется с использованием HTTP команд GET, DELETE, PUT. Для создания нового элемента используется HTTP команда POST (в нашем примере к адресу /user/ - ID создаваемого элемента до момента создания мы, как правило, не знаем). Для приложений, функциональность которых не выходит за рамки CRUD, а можно уверенно сказать, что это большинство современных веб приложений, RESTful подход является лучшим выбором для создания веб сервисов сервисов и API.



В данной статье я предлагаю рассмотреть создание простейшего RESTful приложения с использованием ExtJS 3 на клиентской стороне. Серверную часть можно написать на любой любимой технологии - будь то Java, PHP, Python или что-то еще.

В этой статье подразумевается, что Ext уже подключен, также рекомендуется использование связки Firefox + Firebug для отладки Javascript и просмотра http заголовков запросов и ответов. Итак, поехали!

Будем использовать хранилище (Store) с настроенным HttpProxy. Дело в том, что HttpProxy поддерживает api для установки методов загрузки, обновления и удаления данных в хранилищах Store:

var myProxy = new Ext.data.HttpProxy({
    api: {
        read:    {url: 'app/users/', method: 'GET'},
        create:  {url: 'app/users/', read: 'POST'},
        update:  {url: 'app/users/', method: 'PUT'},
        destroy: {url: 'app/users/', method: 'DELETE'}
    }
});

Как видим, можно настроить разные URI адреса для действий чтения, обновления и т.д., однако это плохо согласуется с идеологией REST, поэтому мы будем использовать один адрес с разными передаваемыми параметрами и разными HTTP методами. Стоит отметить, что по умолчанию используется метод POST, поэтому в каждом случае следует вручную указывать параметр method.

Сконфигурируем JsonReader и JsonWriter:

var myReader = new Ext.data.JsonReader({
    successProperty: 'success', // json элемент со статусом
                                // результата (false or true)
    idProperty: 'id',
    root: 'data', // корневой элемент с данными
}, [
    {name: 'id'},
    {name: 'email', allowBlank: false},
    {name: 'first', allowBlank: false},
    {name: 'last', allowBlank: false}
]);

var myWriter = new Ext.data.JsonWriter({
    encode: true,         // важно! кодировать Store
    writeAllFields: false // сохранять только измененные поля
});

Теперь можно сконфигурировать само хранилище. Фактически, мы указываем здесь reader, writer и proxy:

var myStore = new Ext.data.Store({
    id: 'user',
    proxy: myProxy,
    reader: myReader,
    writer: myWriter,
    idProperty: 'id', // важно! Id элементов хранилища
    autoSave: true // при изменении хранилища будет автоматически
                   // сформирован запрос на его сохранение
});

Теперь при изменении хранилища - то есть изменении записей в хранилище - будет автоматически посылаться запрос к серверу для сохранения всех модификаций. Если по каким-то причинам мы хотим отправлять запрос на сохранение текущего состояния хранилища сами, необходимо установить конфигурационный параметр autoSave хранилища в false; в таком случае запрос на сохранение будет сформирован при вызове метода save() хранилища:

myStore.save();

Обратите внимание на параметр idProperty хранилища; в случае некорректной установки этого параметра при сохранении измененного элемента все равно будет создан запрос на создание нового элемента вместо сохранения существующего (POST вместо PUT). Это связано с тем, что каждая запись в хранилище однозначно идентифицируется параметром id, который должен быть связан с реальным идентификатором нашей записи (id в JsonReader - как правило, идентификатор записи в базе данных).

Также обратите внимание, что если используется mapping в параметрах fields для JsonReader - в idProperty нужно указывать реальное название параметра в Json данных, а не название колонки данных в Ext'е. Для тех, кто не в курсе - маппинг это сопоставление имен параметров в Json данных и имен этих же параметров в Ext для манипуляции с ними, при желании можно задавать разные имена. Иными словами, мы могли бы так настроить JsonReader:

var myReader = new Ext.data.JsonReader({
    successProperty: 'success', // json элемент со статусом
                                // результата (false or true)
    idProperty: 'id',
    root: 'data', // корневой элемент с данными
}, [
    {name: 'ExtIdentificator', mapping: 'realJsonId'},
    {name: 'email', allowBlank: false},
    {name: 'first', allowBlank: false},
    {name: 'last', allowBlank: false}
]);

Для idParamater в таком случае надо указывать realJsonId.

Итак, теперь мы умеем сохранять данные хранилища. Но как модифицировать эти данные? Как правило, хранилище используется либо для загрузки данных с сервера с последующим использованием в формах или других элементах страницы, либо для загрузки данных в таблицу Grid. В случае использования EditorGridPanel данные таблицы и данные хранилища полностью синхронизированы, то есть при изменении данных таблицы автоматически меняется и хранилище, с ним связанное. Это первый способ изменения данных хранилища.

Другой способ - ручной. Например, мы хотим обновить данные первого пользователя в хранилище myStore:

myStore.getAt(0).data.items[0].name = 'Alexey';

myStore.getAt(0) возвращает первую запись (Record) хранилища; свойство data записи хранит в себе все данные записи.

Таким образом, изменив одним из описанных способов хранилище, мы имеем возможность сохранить новое состояние хранилища либо автоматически (путем настройки хранилища autoSave: true), либо вручную путем вызова метода хранилища save().

Пример использования JsonWriter можно посмотреть здесь.