В этом примере рассмотрим такой компонент ExtJs, как Ext.DataView. Он очень удобен для отображения списка различных объектов. Вот собственно примеры.
Этот пример продолжение #1 Создание проекта. Исходники примера можно скачать здесь.
Ext.data.DirectStore
Для работы с данными в ExtJs используется Ext.data.Store. Это такой себе аналог Django-вского QuerySet, который получает данные с сервера. Мы будем использовать унаследованный Ext.data.DirectStore, который умеет получать данные через Ext.Direct.
Создадим простую модель для хранения информации о проекте:
class Project(models.Model):
name = models.CharField(max_length=100)
description = models.TextField(blank=True)
users = models.ManyToManyField(User, blank=True)
def __unicode__(self):
return self.name
Теперь в Ext.ux.ProjectsPanel.js определим наш Store для загрузки данных о проектах:
Ext.ns('Ext.ux.stores'); //Объявляем namespace
Ext.ux.stores.ProjectStore = new Ext.data.DirectStore({
root: 'data',
storeId: 'projects',
totalProperty: 'count',
fields: [
'id',
'name',
'description'
],
api: {
read: ProjectApi.read //Метод Ext.Direct, который возвращает данные
}
});
Значение свойств станут понятными далее. Добавим метод store_record, который будет возвращать данные для нашего Store:
class Project(models.Model):
name = models.CharField(max_length=100)
description = models.TextField(blank=True)
users = models.ManyToManyField(User, blank=True)
def __unicode__(self):
return self.name
def store_record(self):
return {
'id': self.id,
'name': self.name,
'description': self.description
}
Теперь в модуль main/prc.py добавим action для работы с проектами. Пока он будет просто содержать метод read, который вернет данные для Ext.ux.stores.ProjectStore:
PROJECTS_ON_PAGE = getattr(settings, 'PROJECTS_ON_PAGE', 10)
class ProjectApiClass(object):
def read(self, rdata, user):
start = int(rdata.get('start', 0))
end = start + int(rdata.get('limit', PROJECTS_ON_PAGE))
qs = Project.objects.all()
data = [item.store_record() for item in qs[start:end]]
#Возвращаем данные для Ext.ux.stores.ProjectStore
#Данные в Ext.ux.stores.ProjectStore.root
#Общее количество в Ext.ux.stores.ProjectStore.totalProperty для
#отображения постранички
return {'data': data, 'count': Project.objects.count()}
read._args_len = 1
Добавим наш action в Router для Ext.Direct:
class Router(RpcRouter):
def __init__(self):
self.url = 'main:router'
self.actions = {
'MainApi': MainApiClass(),
'ProjectApi': ProjectApiClass()
}
self.enable_buffer = 50
Теперь можем использовать метод ProjectApi.read() для получения данных о проектах. Его мы и указали в Ext.ux.stores.ProjectStore.api.read.
Ext.ux.ProjectsView
Приступим к созданию DataView в Ext.ux.ProjectsPanel.js. Для начала создадим шаблон для рендеринга элемента. Будем использовать Ext.XTemplate. Понять как он работает можно из описания конструктора, см. метод XTemplate.
Ext.ns('Ext.ux.tpl');
Ext.ux.tpl.ProjectTpl = new Ext.XTemplate(
'<tpl for=".">',
'<div class="project-wrap" pr_id="{id}">',
'<h3>{name}</h3>',
'<p>{description}</p>',
'</div>',
'</tpl>'
);
Кое какие стили. .x-view-selected - это класс, который добавляется к выбранному элементу.
.project-wrap {
margin-bottom: 10px;
}
.project-wrap h3 {
cursor: pointer;
}
h3.x-view-selected {
color: #008080;
}
Ну и сам Ext.ux.ProjectsView.
Ext.ux.ProjectsView = Ext.extend(Ext.DataView, {
singleSelect: true, //только один элемент может быть выбран
store: 'projects', //id нашего Store для загрузки данных, можно передать сам Store
tpl: Ext.ux.tpl.ProjectTpl, //шаблон для рендеринга элемента
emptyText: 'No projects',
loadingText: 'Loading...',
autoScroll: true, //обображаем скролл, если элементы не влязят в панель
itemSelector: 'h3', //DOM-элемент который будет отвечать за выбор элемента
initComponent: function(){
Ext.ux.ProjectsView.superclass.initComponent.call(this);
this.on('selectionchange', this.onSelect, this); //добавляем обработчик выбора элемента
},
afterRender: function(){
//этод метод выполняеться после рендеринга компонента
//можно использовать пости для всех компонентов ExtJs(Ext.Panel, Ext.Window и др.)
Ext.ux.ProjectsView.superclass.afterRender.apply(this, arguments);
this.reload();
},
reload: function(){
//обновляем данные с сервера
this.store.load();
},
onSelect: function(view, selected){
//обработчик выбора элемента
//проверяем выбран ли элемент т.к. метод вызывается если выбор снят
if (selected.length){
//В selected[0] будет DOM-елемент, который указали в itemSelector
//метод Ext.data.Store.getRecord возвращает запись соответствующую
//записаь для этого элемента
var r = this.getRecord(selected[0])
//Показываем имя проекта,
//аналогично можно получить id, description или другое поле
alert(r.get('name'))
}
}
});
Для выбора элемента просто кликаем на заголовок. Что бы отменить выбор кликаем с Ctrl. Если указать multiSelect=False, тогда можно будет выбирать несколько элементов через Shift. С помощью шаблона можно создавать сложные панели для отображения сложных объектов. Следует учитывать, что DataView "связывает" элемент с его отображением через порядковый номер. То есть если в шаблоне поменять порядок отображения элементов через условия, то в выбранные записи могут быть совсем не те, которые ожидаются. Выход - отсортировать на сервере у нужном порядке. Зачем менять порядок отображения? Например мы хотим разбить отображения проектов на "наши" и "чужие". Используем два "[HTML_REMOVED]", каждый отображает нужные элементы в нужном виде. В afterRender можно навешать обработчики на click и др. Например добавить кнопочку редактирования, удаления и др и обрабатывать их нажатие. Например вот так:
this.getEl().on('dblclick', this.onEdit, this, {
delegate: '.project-wrap'
});
Ext.ux.ProjectsPanel
Создадим панель для Ext.ux.ProjectsView. Сам Ext.DataView не наследуется от Ext.Panel, поэтому мы не можем указать title или toolbar.
Ext.ux.ProjectsPanel = Ext.extend(Ext.Panel, {
autoScroll: true,
title: 'Projects',
initComponent: function(){
this.projects_view = new Ext.ux.ProjectsView(); //сохраним на будущее
var config = {
items: this.projects_view,
bbar: new Ext.PagingToolbar({ //paginator
store: 'projects',
displayInfo: true,
pageSize: PROJECTS_ON_PAGE
})
};
Ext.apply(this, Ext.apply(this.initialConfig, config));
Ext.ux.ProjectsPanel.superclass.initComponent.apply(this, arguments);
}
});
Ext.reg('ext:ux:projects-panel', Ext.ux.ProjectsPanel);
В нижний tollbar панели мы добавили paginator. Просто указали каким Store он должен "рулить". PROJECTS_ON_PAGE передали в шаблон, может и не очень красиво:
var PROJECTS_ON_PAGE = {{ PROJECTS_ON_PAGE }};
Ext.reg регистрирует наш компонент в глобальный менеджер компонентов. Теперь можно просто указать xtype: 'ext:ux:projects-panel', как мы сделаем далее. Так зарегистрированы все компоненты ExtJs. Раньше в API указывался xtype, не знаю зачем убрали. Можно посмотреть в исходниках(Defined In). Вот например для Ext.form.TimeField в самом низу видим:
Ext.reg('timefield', Ext.form.TimeField);
Добавим панель в Ext.ux.MainViewport. В config.items добавляем следующее:
{
region: 'east',
width: 350,
xtype: 'ext:ux:projects-panel'
}
Это равносильно:
new Ext.ux.ProjectsPanel({
region: 'east',
width: 350
})
Должно получиться как-то так: