Фактически для динамического создания колонок мы должны передать метаданные и построить по ним модель колонок. Метаданные передаются JsonReader'у вместе с данными и начинаются со слова "metaData", они содержат параметры для конфигурации reader'а, полей и могут содержать произвольные данные.
При чтении наш JsonReader должен выделить метаданные, сконфигурировать себя, заполнить модель колонок и массив полей.
Массив полей он заполнит и без нашего участия, а вот модель колонок придется создавать ручками.
При чтении наш JsonReader должен выделить метаданные, сконфигурировать себя, заполнить модель колонок и массив полей.
Массив полей он заполнит и без нашего участия, а вот модель колонок придется создавать ручками.
Согласно документации метаданные это объект в формате JSON, который содержит информацию о пакете с данными:
- "root" - корень, с какого параметра начинать читать сами данные
- "totalProperty" - где описано количество записей в наборе
- "successProperty" - параметр, по которому reader поймет, что все OK
- "fields" - объект, который содержит описание полей
Полное описание метаданных в help'e, а выглядят метаданные примерно следующим образом:
{
// описываем метаданные
metaData: {
"idProperty": "id",
"root": "rows",
"totalProperty": "total"
"successProperty": "success",
"fields": [
{"name": "name"},
{"name": "job", "mapping": "occupation"}
],
// произвольные объекты, например, сюда мы можем отправить модель колонок
"columns": [ модель колонок ]
},
// эти свойства относятся к данным
"success": true, // Reader's configured successProperty
"results": 2000, // Reader's configured totalProperty
// сами данные
"rows": [ // *Note: this must be an Array
{ "id": 1, "name": "Bill", "occupation": "Gardener" },
{ "id": 2, "name": "Ben", "occupation": "Horticulturalist" }
]
}
Вопрос номер один - где хранить данные о колонках? Вариантов, как минимум, два. Во-первых, мы можем хранить данные о колонках в отдельном произвольном объекте "columns": [ модель колонок ] или отправлять всю информацию в том же объекте fields. По некоторому размышлению, я пришла к выводу, что для меня удобнее второй вариант.
С учетом сказанного, json данные должны выглядеть примерно так:
{
"metaData": {
"totalProperty": "total",
"successProperty": "success",
"root": "tablerow",
"idProperty": "ID",
"fields": [
// у поля ID определяем заголовок и ширину
{"name": "ID","type": "string", "mapping": "ID", "header": "PK", "width" : "80"}
// у поля tdoc - заголовок, ширину и параметры сортировки
{"name": "tdoc", "type": "string", "mapping": "TDOC", "header": "tdoc", "width" : "400", "sortable": "0"}
// у поля nametdoc не определяем ничего
{"name": "nametdoc", "type": "string", "mapping": "NAMETDOC"}
, "success": true,"total": 9,
"tablerow":[
{"ID":1,"TDOC":1,"NAMETDOC":"name tdoc 1"}
, {"ID":2,"TDOC":2,"NAMETDOC":"name 2"}]
}
JsonReader прочитает наши метаданные, настроится, создаст массив полей. Все это действо происходит внутри класса:
Ext.extend(Ext.data.JsonReader, Ext.data.DataReader,
...
readRecords: function(a){
this.jsonData = a;
if (a.metaData)...
buildExtractors: function(){...
Каждый раз, когда приходит пакет с сервера, JsonReader пытается выделить метаданные (metaData), это означает, что изменить метаданные мы можем в любой момент, "на лету". Если в пакете обнаруживаются метаданные, то происходит переконфигурация хранилища (store), связанного с нашим reader'ом, и возбуждается событие metachange, при этом в качестве входных параметров передаются метаданные.
metachange : ( Store this, Object meta )
!!! Событие metachange возбуждается только в JsonReader'e, другие типы хранилищ таким преимуществом не обладают.
!!! Переконфигурация хранилища (store) может привести к ошибкам, это связанно с тем, что некоторые поля, объявленные в reader'е могут отсутствовать, поэтому help от extjs настоятельно рекомендует объявлять JsonReader следующим образом:
var ReaderData= new Ext.data.JsonReader();
Теперь необходимо отловить событие metachange. Ставим слушателя (listeners) для нашего хранилища
var dataStore = new Ext.data.Store ({
// ** конфигурация хранилища store
...
//** PROXY *************
url: 'model-tdoc.php',
reader: ReaderData, // наш JsonReader
writer: Writer,
listeners: {
load : function(obj, records) {
...
} // eo load
, metachange: function(obj, meta) {
console.info('Пришли метаданные');
var m = {};
// можем использовать объект meta, к-й передается в функцию
m = meta;
// или обратиться к метаданным т.о.
m = this.reader.meta;
// смотрим что получили
console.info(Ext.util.JSON.encode(m));
// можно вывести значение объекта totalProperty ...
console.info(m.totalProperty);
// цикл по массиву fields
for (var i = 0; i < m.fields.length; i++ ) {
console.info('поле '+ m.fields[i].name
+ ' заголовок ' + m.fields[i].header
);
} // eo for
} // eo metachange
} // eo listener
}); // eo store
Пока мы написали небольшой экзерсис, дабы потренироваться в получении и обработке метаданных.
Следующий шаг - построить модель колонок ColumnModel и переконфигурировать наш grid.
Чтобы применить к гриду новую модель колонок необходимо воспользоваться методом
reconfigure( Ext.data.Store store, Ext.grid.ColumnModel colModel ) : void
Этот метод позволяет "на лету" изменить свойства гриды для использования другого хранилища (store) и (или) другой модели колонок. Метод возбуждает событие 'reconfigure', связывает новые параметры с гридом и обновляет его.
В нашем случае, код слушателя (listener) события metachange измениться следующим образом:
..., metachange: function(obj, meta) {
console.info('получили метаданные');
// функция построения модели колонок по метаданным
cm = SetColModel(meta);
var grid = Ext.getCmp('grid'); // получить ссылку на grid
// применить новые колонки к гриду
grid.reconfigure(this, cm);
} ...
Для того, чтобы это заработало необходимо также определить "пустую" модель колонок ...
var def_cm = new Ext.grid.ColumnModel({
id: 'cm',
// свойства по умолчанию
defaults: {
,filterable: false // отключаем фильтрацию, иначе ругается
},
columns: [] // пустой массив !!!
});
... и связать grid с этой моделью колонок
var grid = new Ext.grid.EditorGridPanel({
id: 'grid',
store: dataStore, // хранилище
cm: def_cm, // пустая модель колонок
...
});
Примечание. В принципе, создание пустой модели колонок вовсе не обязательно, просто один из плагинов, который я подключила очень недоволен тем фактом, что колонок нет.
Теперь собственно функция, которая будет брать метаданные и строить по ним новую модель колонок
function SetColModel(obj, meta) {
// значения по умолчанию
var defs = {
filterable: true
, width: 120
}
var cols = [];
// заполняем массив колонок
Ext.each(meta.fields, function(fld, idx){
cols[idx] = {
// если заголовок не указан, будем использовать имя поля
header: fld.header ? fld.header : fld.name
, dataIndex: fld.name
// если sortable=0, то false, в остальных случаях true
, sortable: parseInt(fld.sortable) == 0 ? false : true
// если ширина не указана, берем из defs
, width: parseInt(fld.width) ? parseInt(fld.width) : defs.width
...
}
}); // eo each
var cm = new Ext.grid.ColumnModel({
...
}); // eo cm
cm.setConfig(cols); // устанавливаем конфигурацию колонок
//console.info(cm);
return cm;
};
Тестовая функция php, которая отправляет данные и метаданные
<?php
$out = getRecordsWithMetadata();
//********************* отправляем РЕЗУЛЬТАТ ************************************************
echo $out;
function getRecordsWithMetadata(){
// это метаданные !!! не забываем, никаких "левых" символов быть не должно, ака перенос строки и прочее
$meta = '{'
. '"metaData": {totalProperty: "total", "successProperty": "success", "root": "tablerow","idProperty": "ID",'
.'"fields":[ '
.'{"name": "ID","type": "string", "mapping": "ID", "header": "PK", "width" : "80"} '
.',{"name": "tdoc", "type": "string", "mapping": "TDOC", "header": "tdoc", "width" : "400", "sortable": "0"} '
.',{"name": "nametdoc", "type": "string", "mapping": "NAMETDOC"} '
.'] '
.' }';
// а это данные
$out =', "success": true,"total": 9, "tablerow":[ {"ID":1,"TDOC":1,"NAMETDOC":"name tdoc 1"} , {"ID":2,"TDOC":2,"NAMETDOC":"name 2"}]}';
// возвращаем результат
return $meta . $out;
} // eo function getRecordsWithMetadata
?>
Теперь необходимо отловить событие metachange. Ставим слушателя (listeners) для нашего хранилища
var dataStore = new Ext.data.Store ({
// ** конфигурация хранилища store
...
//** PROXY *************
url: 'model-tdoc.php',
reader: ReaderData, // наш JsonReader
writer: Writer,
listeners: {
load : function(obj, records) {
...
} // eo load
, metachange: function(obj, meta) {
console.info('Пришли метаданные');
var m = {};
// можем использовать объект meta, к-й передается в функцию
m = meta;
// или обратиться к метаданным т.о.
m = this.reader.meta;
// смотрим что получили
console.info(Ext.util.JSON.encode(m));
// можно вывести значение объекта totalProperty ...
console.info(m.totalProperty);
// цикл по массиву fields
for (var i = 0; i < m.fields.length; i++ ) {
console.info('поле '+ m.fields[i].name
+ ' заголовок ' + m.fields[i].header
);
} // eo for
} // eo metachange
} // eo listener
}); // eo store
Пока мы написали небольшой экзерсис, дабы потренироваться в получении и обработке метаданных.
Следующий шаг - построить модель колонок ColumnModel и переконфигурировать наш grid.
Чтобы применить к гриду новую модель колонок необходимо воспользоваться методом
reconfigure( Ext.data.Store store, Ext.grid.ColumnModel colModel ) : void
Этот метод позволяет "на лету" изменить свойства гриды для использования другого хранилища (store) и (или) другой модели колонок. Метод возбуждает событие 'reconfigure', связывает новые параметры с гридом и обновляет его.
В нашем случае, код слушателя (listener) события metachange измениться следующим образом:
..., metachange: function(obj, meta) {
console.info('получили метаданные');
// функция построения модели колонок по метаданным
cm = SetColModel(meta);
var grid = Ext.getCmp('grid'); // получить ссылку на grid
// применить новые колонки к гриду
grid.reconfigure(this, cm);
} ...
Для того, чтобы это заработало необходимо также определить "пустую" модель колонок ...
var def_cm = new Ext.grid.ColumnModel({
id: 'cm',
// свойства по умолчанию
defaults: {
,filterable: false // отключаем фильтрацию, иначе ругается
},
columns: [] // пустой массив !!!
});
... и связать grid с этой моделью колонок
var grid = new Ext.grid.EditorGridPanel({
id: 'grid',
store: dataStore, // хранилище
cm: def_cm, // пустая модель колонок
...
});
Примечание. В принципе, создание пустой модели колонок вовсе не обязательно, просто один из плагинов, который я подключила очень недоволен тем фактом, что колонок нет.
Теперь собственно функция, которая будет брать метаданные и строить по ним новую модель колонок
function SetColModel(obj, meta) {
// значения по умолчанию
var defs = {
filterable: true
, width: 120
}
var cols = [];
// заполняем массив колонок
Ext.each(meta.fields, function(fld, idx){
cols[idx] = {
// если заголовок не указан, будем использовать имя поля
header: fld.header ? fld.header : fld.name
, dataIndex: fld.name
// если sortable=0, то false, в остальных случаях true
, sortable: parseInt(fld.sortable) == 0 ? false : true
// если ширина не указана, берем из defs
, width: parseInt(fld.width) ? parseInt(fld.width) : defs.width
...
}
}); // eo each
var cm = new Ext.grid.ColumnModel({
...
}); // eo cm
cm.setConfig(cols); // устанавливаем конфигурацию колонок
//console.info(cm);
return cm;
};
Тестовая функция php, которая отправляет данные и метаданные
<?php
$out = getRecordsWithMetadata();
//********************* отправляем РЕЗУЛЬТАТ ************************************************
echo $out;
function getRecordsWithMetadata(){
// это метаданные !!! не забываем, никаких "левых" символов быть не должно, ака перенос строки и прочее
$meta = '{'
. '"metaData": {totalProperty: "total", "successProperty": "success", "root": "tablerow","idProperty": "ID",'
.'"fields":[ '
.'{"name": "ID","type": "string", "mapping": "ID", "header": "PK", "width" : "80"} '
.',{"name": "tdoc", "type": "string", "mapping": "TDOC", "header": "tdoc", "width" : "400", "sortable": "0"} '
.',{"name": "nametdoc", "type": "string", "mapping": "NAMETDOC"} '
.'] '
.' }';
// а это данные
$out =', "success": true,"total": 9, "tablerow":[ {"ID":1,"TDOC":1,"NAMETDOC":"name tdoc 1"} , {"ID":2,"TDOC":2,"NAMETDOC":"name 2"}]}';
// возвращаем результат
return $meta . $out;
} // eo function getRecordsWithMetadata
?>
Пишем новое расширение.
Задача. Создать новое расширение. Родителем выберем Ext.grid.GridPanel.
Напишем инициализацию, для того, чтобы иметь возможность переопределять в наследниках url и др.
Перекроем метод onRender, где будем вызывать загрузку хранилища (store) и функцию динамического построения колонок.
// ***** динамический грид *******************************//
DynamicGrid = Ext.extend(Ext.grid.GridPanel, {
// применяем конфигурацию
initComponent: function(){
var config = {
viewConfig: {
forceFit: false
},
enableColLock: false,
loadMask: true,
border: true,
// ... и другие
ds: new Ext.data.Store({
url: this.storeUrl, // url, который будет передан наследникам этого класса
reader: new Ext.data.JsonReader() // пустой reader
}),
// модель колонок по умолчанию
cm: new Ext.grid.ColumnModel({
defaults: {
sortable: true //
, filterable: true
, width: 100
},
columns: [] // пустые колонки
})
};
// применяем конфигурацию
Ext.apply(this, config);
Ext.apply(this.initialConfig, config);
DynamicGrid.superclass.initComponent.apply(this, arguments);
},
onRender: function(ct, position){
// вызываем метод предка
DynamicGrid.superclass.onRender.call(this, ct, position);
// обрабатываем событие on load для store
this.store.on('load', function(){
console.info('грузим store');
// !!! Здесь получаем новую модель колонок
this.reconfigure(this.store, DynamicColumnModel(this.store));
}, this);
// грузим данные в store
this.store.load({ params: { start: 0, limit: 10} });
}
}); // eo dynamic grid
Задача. Создать новое расширение. Родителем выберем Ext.grid.GridPanel.
Напишем инициализацию, для того, чтобы иметь возможность переопределять в наследниках url и др.
Перекроем метод onRender, где будем вызывать загрузку хранилища (store) и функцию динамического построения колонок.
// ***** динамический грид *******************************//
DynamicGrid = Ext.extend(Ext.grid.GridPanel, {
// применяем конфигурацию
initComponent: function(){
var config = {
viewConfig: {
forceFit: false
},
enableColLock: false,
loadMask: true,
border: true,
// ... и другие
ds: new Ext.data.Store({
url: this.storeUrl, // url, который будет передан наследникам этого класса
reader: new Ext.data.JsonReader() // пустой reader
}),
// модель колонок по умолчанию
cm: new Ext.grid.ColumnModel({
defaults: {
sortable: true //
, filterable: true
, width: 100
},
columns: [] // пустые колонки
})
};
// применяем конфигурацию
Ext.apply(this, config);
Ext.apply(this.initialConfig, config);
DynamicGrid.superclass.initComponent.apply(this, arguments);
},
onRender: function(ct, position){
// вызываем метод предка
DynamicGrid.superclass.onRender.call(this, ct, position);
// обрабатываем событие on load для store
this.store.on('load', function(){
console.info('грузим store');
// !!! Здесь получаем новую модель колонок
this.reconfigure(this.store, DynamicColumnModel(this.store));
}, this);
// грузим данные в store
this.store.load({ params: { start: 0, limit: 10} });
}
}); // eo dynamic grid
Ссылки по теме: