Диалог
Основные понятия:
Диалог
Диалог выбора сущностей представляет собой всплывающее (pop-up) окно со списком произвольных элементов, которые могут быть сгруппированы по вкладкам:
Дополнительно диалог может иметь встроенный поиск с возможностью создания новых элементов, а также специальный футер, в котором можно разместить ссылки на внешние интерфейсы.
Элементы
Элемент — это пункт списка.
Как правило, элементы отображаются в конкретных вкладках, но это условие необязательное. Если элемент не привязан ни к одной вкладке, его всего равно можно найти через поиск.
Каждый элемент уникально идентифицируется двумя атрибутами: id
и entityId
:
id
— числовой или строковый идентификатор элемента.entityId
— строковый идентификатор сущности.
Атрибут entityId
определяет принадлежность элемента к конкретной сущности, поэтому диалог позволяет одновременно отображать и выбирать элементы разных сущностей.
const dialog = new Dialog({ items: [ { id: 1, entityId: 'my-entity', title: 'Пункт A', tabs: 'my-tab' }, { id: 2, entityId: 'my-entity', title: 'Пункт B', tabs: 'my-tab' }, { id: 3, entityId: 'my-entity', title: 'Пункт C', tabs: 'my-tab' }, { id: 1, entityId: 'my-user', title: 'Иван Иванов', tabs: 'my-tab' }, { id: 2, entityId: 'my-user', title: 'Константин Константинопольский', tabs: 'my-tab' }, { id: 'USD', entityId: 'my-currency', title: 'Американский Доллар', tabs: 'my-tab' }, ], tabs: [ { id: 'my-tab', title: 'Моя вкладка' } ], showAvatars: false, dropdownMode: true }); dialog.show();
Сущности
Сущность — это уникальный тип элемента, который может определять:
- Внешний вид элементов в диалоге (аватар по умолчанию, цвет заголовка и др.).
- Внешний вид элементов в виджете TagSelector.
- Провайдер данных — PHP-класс, который заполняет диалог данными с бэкенда.
- Настройки полей для поиска.
Настройки сущностей определяются либо глобально (рекомендуется), либо непосредственно при создании объекта диалога (опция entities).
Добавление элементов в диалог
Наполнение диалога элемента происходит двумя способами:
- Статически — данные элементов и вкладок непосредственно передаются в конструктор класса Dialog.
const button = document.getElementById('button'); const dialog = new Dialog({ targetNode: button, width: 400, height: 300, dropdownMode: true, showAvatars: false, compactView: true, tabs: [ { id: 'cities', title: 'Города', itemOrder: { 'title': 'asc' } }, { id: 'countries', title: 'Страны', itemOrder: { 'title': 'asc' } }, ], items: [ { id: 1, entityId: 'city', tabs: 'cities', title: 'Калининград' }, { id: 2, entityId: 'city', tabs: 'cities', title: 'Москва' }, { id: 3, entityId: 'city', tabs: 'cities', title: 'Киев' }, { id: 4, entityId: 'city', tabs: 'cities', title: 'Санкт-Петербург' }, { id: 5, entityId: 'city', tabs: 'cities', title: 'Минск' }, { id: 6, entityId: 'city', tabs: 'cities', title: 'Гданьск' }, { id: 7, entityId: 'city', tabs: 'cities', title: 'Варшава' }, { id: 8, entityId: 'city', tabs: 'cities', title: 'Берлин' }, { id: 1, entityId: 'country', tabs: 'countries', title: 'Российская Федерация' }, { id: 2, entityId: 'country', tabs: 'countries', title: 'Соединенные Штаты Америки' }, { id: 3, entityId: 'country', tabs: 'countries', title: 'Украина' }, { id: 4, entityId: 'country', tabs: 'countries', title: 'Беларусь' }, { id: 5, entityId: 'country', tabs: 'countries', title: 'Польша' }, { id: 6, entityId: 'country', tabs: 'countries', title: 'Казахстан' }, ], }); dialog.show();
- Динамически — данные загружаются с бэкенда с помощью провайдеров данных.
const button = document.getElementById('button'); const dialog = new Dialog({ targetNode: button, enableSearch: true, context: 'MY_MODULE_CONTEXT', entities: [ { id: 'user', // пользователи }, { id: 'department', // структура компании: выбор только пользователей }, ], }); dialog.show();
Во время открытия диалог выполнит запрос на бэкенд и получит данные от соответствующих провайдеров. Необходимость выполнения запроса на бэкенд определяется наличием сущностей (опция
entities
), у которых свойство dynamicLoad установлено в значениеtrue
. Для стандартных сущностейdynamicLoad
установлен глобально, поэтому явно прописывать эту опцию не следует (см. пример выше).
Оба подхода можно использовать одновременно.
DOM-узлы элемента
Каждый элемент диалога может иметь несколько визуальных представлений. Это дает возможность отображать один и тот же элемент на разных вкладках или на разных уровнях древовидного представления.
Элемент диалога представлен классом Item, а его визуальные представления — классом ItemNode. Другими словами, класс ItemNode представляет DOM-узел элемента.
Данные (заголовок, аватар и др.), которые отображает DOM-узел, могут быть заданы как для конкретного представления, так и для элемента — в этом случае все DOM-узлы будут иметь одинаковый вид. Также есть возможность задать визуальные параметры для элемента глобально в настройках сущности.
Вкладки
Вкладки позволяют группировать элементы диалога в группы:
По умолчанию диалог создает две системных вкладки: "Последние" и "Поиск". Вкладка "Последние" отображает элементы (при динамической загрузке), которые пользователь выбирал в последний раз. Вкладка "Поиск" скрыта по умолчанию и активируется только в момент набора поисковой фразы.
Если элементов на вкладке нет, то вместо пустого списка отображается "заглушка". Эту "заглушку" можно кастомизировать по своему желанию.
Вкладка "Последние"
Когда пользователь выбирает элементы в диалоге, этот факт запоминается на бэкенде. В следующий раз при открытии диалога эти элементы будут автоматически показаны на вкладке "Последние".
Выбранные пользователем элементы запоминаются в рамках контекста — символьного идентификатора, который указывается при создании диалога в опции context. С помощью идентификатора контекста можно как создавать разные списки "последних" элементов, так и делать их одинаковыми в рамках, например, одного сервиса.
Примечание: Если при создании диалога контекст не указан, выбранные элементы запоминаться не будут.
Один и тот же элемент может быть выбран в разных диалогах и в разных контекстах. Чтобы можно было гибко управлять (с помощью провайдера данных) списком элементов вкладки "Последние", существуют два вида контекстов:
- Локальный (текущий) контекст — контекст (указывается при создании диалога), для которого автоматически выбираются элементы на вкладке "Последние".
Для работы с элементами локального контекста в провайдере есть следующие методы:
- Глобальный контекст — все остальные контексты, в которых пользователь выбирал элементы указанных сущностей.
Чтобы получить элементы из глобального контекста в провайдере данных, используйте метод getGlobalRecentItems().
Поиск
Форма поиска включается опцией enableSearch: true:
По умолчанию поиск осуществляется по всем элементам диалога. Отменить поиск можно глобально для всех элементов сущности (опция searchable), а также для конкретного элемента (аналогичная опция searchable).
Поисковая фраза ищется в заголовке и подзаголовке элемента. Расширить поля для поиска, а также указать их приоритет можно с помощью опции searchFields в настройках сущности.
Создание нового элемента из поиска
Дополнительно с помощью формы поиска можно создать новый элемент:
Данная возможность включается с помощью опции searchOptions.allowCreateItem.
Создание нового элемента делегируется внешнему коду, который выполняется на событии Search:onItemCreateAsync.
const button = document.getElementById('button'); button.addEventListener('click', function() { dialog.show(); }); const dialog = new Dialog({ targetNode: button, width: 400, height: 300, dropdownMode: true, enableSearch: true, compactView: true, showAvatars: false, tabs: [ { id: 'cities', title: 'Города', itemOrder: { sort: 'asc', title: 'asc' } }, { id: 'countries', title: 'Страны', itemOrder: { 'title': 'asc' } }, ], items: [], searchOptions: { allowCreateItem: true, footerOptions: { label: 'Создать город:' } }, events: { 'Search:onItemCreateAsync': (event) => { return new Promise((resolve) => { const { searchQuery } = event.getData(); const dialog = event.getTarget(); setTimeout(() => { // эмуляция асинхронного действия let tab = dialog.getTab('cities'); const item = dialog.addItem({ id: Text.getRandom(), entityId: 'city', title: searchQuery.getQuery(), tabs: 'cities', // можно использовать для сортировки элементов на вкладке // для вкладки cities указана сортировка по этому полю. sort: 1 }); if (item) { item.select(); } dialog.selectTab(tab.getId()); resolve(); }, 1000); }); } } });
Кеширование поисковых запросов
По умолчанию диалог кеширует поисковые запросы для того, чтобы уменьшить количество обращений на бэкенд. Считается, что при обработке поискового запроса на бэкенде провайдер отдает полностью все данные по указанной поисковой фразе. Например, если провайдер вернул данные по фразе "Иван", то по фразе "Иванов" диалог уже не будет делать запрос на бэкенд.
Отменить алгоритм кеширования по умолчанию можно двумя способами:
- Метод $searchQuery->setCacheable(false) позволяет отменить кеширование текущего запроса.
- Опция searchCacheLimits позволяет определить шаблоны регулярных выражений, по которым не будет происходить кеширование.