четверг, 26 февраля 2015 г.

Дерево как модель, хранилище и панель

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


Модель

Наследуется от TreeModel. Легко по ошибке унаследовать от обычной модели, тогда появляются странные ошибки в консоли.

Прокси для предпочтительнее определять в модели. Хранилища умеют пользоваться ими тоже. Для обычной таблицы достаточно вместо JSON-объекта вернуть массив, чтобы в хранилище было создано несколько записей вместо одной. У деревьев иной принцип. Они загружают дочерние узлы для корневого узла, который определяется в конфигурации хранилища.

Модели типа TreeModel предоставляют интерфейс узлов дерева - NodeInterface. Помимо объявленных в определении полей ExtJs автоматически создаёт дополнительные поля, такие как loaded, expanded и проч. Важно, чтобы не возник конфликт в названии имён. Например, при отображении файлового хранилища из таблицы базы данных может считываться поле loaded, которое содержит дату загрузки файла пользователем. ExtJs поймёт это поле по-своему: поступившая с сервера запись уже присутствует в хранилище и повторно загружать её не следует. Некоторые поля интерфейса NodeInterface придётся дописывать на стороне сервера. По умолчанию узлы дерева отображаются в виде папок. Чтобы отобразить узел в виде документа, нужно на сервере дописать параметр leaf со значением true. Чтобы папка отразилась в развёрнутом виде, нужно добавить параметр expanded = true. Иногда можно определить значение этих полей на стороне браузера. В этом случае задействуется свойство ридера transform.

Хранилище

Сервер должен присылать данные в определённом формате. Бывает не сразу понятно, откуда берутся названия полей. Ниже показаны настройки древовидного хранилища, которые можно было бы и не указывать, но при объяснении они значительно облегчают понимание. Обратите внимание на defaultRootProperty и на параметр rootProperty в определении прокси, данном выше. Параметр nodeParam актуален для AJAX-прокси. при первой загрузке данных в хранилище на сервер уйдёт GET-запрос, в котором node=0, а не id=0, как можно было бы решить на основании нижеследующего определения.

Может случится так, что узлы верхнего уровня в таблице базы данных имеют parentId = NULL. Обязательно нужно преобразовать NULL в 0, иначе эти узлы не отобразятся внутри корневого элемента, имеющего id=0.

Свойство parentIdProperty позволяет отсылать с сервера данные в виде "плоской" структуры, а не вложенной, использующей параметр children. К сожалению, браузер не запросит родительские узлы автоматически, поэтому смысла в нём я вижу мало. Принцип загрузки не изменяется. Всё равно нужно предоставлять в ответе сервера данные всех дочерних и родительских узлов.

Панель

 Про панель нужно знать, что она близка по возможностям к обычной таблице. Наряду с колонкой типа treecolumn, можно добавить и другие типы колонок. В колонке, отображающей вложенную структуру обычно показывается несколько полей модели, поэтому вместо dataIndex внизу используется настройка renderer.

Некоторые вещи достигаются через viewConfig или через getView. Например, прокрутка к найденному узлу(=строке): myTreePanel.getView().focusRow(myNodeRecord).



Результат

Щелчёк по папке загружает новую порцию дочерних узлов.


Первый запрос


Ответ сервера



Фильтр при помощи store.load()



Фильтр при помощи store.setFilters()

Фильтр при помощи store.setRoot()


Ответ сервера при фильтрации



5 комментариев:

  1. Александр, спасибо за статью.. достаточно сжато - но осилил :)
    Скажите, приходилось ли Вам использовать в проетах - Ext JS State Provider ?
    Было бы здорово, если сможете "на пальцах" объяснить работу этого компонента.

    ОтветитьУдалить
    Ответы
    1. Николай, State Provider не использовал пока, но планирую, потому что вещь полезная. Позволяет сохранить и восстановить состояние графического интерфейса при повторном входе пользователя. Данные о предметной области сохраняются в моделях и хранилищах, данные о способе их отображения у конкретного пользователя попадают в State Provider. Будет что написать по этой теме, напишу обязательно.

      Удалить
    2. Спасибо. Тема очень занятная для использования в разработке.. хотя я только учусь :)

      Удалить
    3. Нашел что-то подобное - http://miamicoder.com/2015/creating-a-custom-ext-js-state-provider/

      Удалить

  2. var store = Ext.data.StoreManager.lookup('structure');

    store.setRoot({
    id: 1
    });

    store.load({
    params: {
    cluster: true
    },
    callback: function () {
    var root = store.getRoot();
    this.walk(root);
    $("#orgchart").getOrgChart('draw');
    this.addHandlers(root);
    },
    scope: this
    });

    ОтветитьУдалить