735 lines
23 KiB
Markdown
735 lines
23 KiB
Markdown
# Документация библиотеки CRUD Dialogs
|
||
|
||
Библиотека для создания модальных диалоговых окон на основе Bootstrap с поддержкой CRUD операций, вкладок и гибкой настройкой макета.
|
||
|
||
## Оглавление
|
||
|
||
1. [Подключение](#подключение)
|
||
2. [Основные концепции](#основные-концепции)
|
||
3. [API Reference](#api-reference)
|
||
- [makeDialog](#makedialog)
|
||
- [Методы объекта диалога](#методы-объекта-диалога)
|
||
- [Глобальные методы](#глобальные-методы)
|
||
4. [Типы полей](#типы-полей)
|
||
5. [Настройки диалога](#настройки-диалога)
|
||
6. [Layout и категории](#layout-и-категории)
|
||
7. [Примеры использования](#примеры-использования)
|
||
|
||
---
|
||
|
||
## Подключение
|
||
|
||
```html
|
||
<!-- Зависимости -->
|
||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
|
||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
|
||
|
||
<!-- Библиотека dom.js (должна быть подключена первой) -->
|
||
<script src="dom.js"></script>
|
||
|
||
<!-- Библиотека CRUD Dialogs -->
|
||
<script src="crud_d.js"></script>
|
||
```
|
||
|
||
---
|
||
|
||
## Основные концепции
|
||
|
||
Библиотека работает с тремя основными режимами открытия диалога:
|
||
|
||
- **create** — создание нового элемента (пустая форма)
|
||
- **update** — редактирование существующего элемента (форма с подгруженными данными)
|
||
- **create from** — создание нового элемента из существующего (форма с подгруженными данными)
|
||
|
||
Каждый диалог имеет:
|
||
- Уникальный строковый идентификатор (dialogId)
|
||
- Набор полей с типами и настройками
|
||
- Эндпоинты для CRUD операций (устанавливаются отдельно)
|
||
- Опциональные настройки отображения (ширина, категории)
|
||
|
||
Управлять диалогом можно:
|
||
- Через объект, полученный при создании
|
||
- Через глобальные методы, используя dialogId
|
||
|
||
---
|
||
|
||
## API Reference
|
||
|
||
### makeDialog
|
||
|
||
Создает новый диалог и возвращает объект для работы с ним.
|
||
|
||
```javascript
|
||
crud_d.makeDialog(dialogId, fields, options)
|
||
```
|
||
|
||
**Параметры:**
|
||
|
||
| Параметр | Тип | Обязательный | Описание |
|
||
|----------|-----|--------------|----------|
|
||
| dialogId | string | Да | Уникальный идентификатор диалога (не DOM id) |
|
||
| fields | object | Да | Объект с описанием полей |
|
||
| options | object | Нет | Дополнительные настройки диалога |
|
||
|
||
**Возвращает:** Объект диалога с методами управления.
|
||
|
||
**Пример:**
|
||
```javascript
|
||
const dialog = crud_d.makeDialog('user_form', {
|
||
name: ['text', { label: 'Имя', required: true }, ''],
|
||
email: ['email', { label: 'Email', required: true }, '']
|
||
}, {
|
||
width: 'lg'
|
||
});
|
||
```
|
||
|
||
---
|
||
|
||
### Методы объекта диалога
|
||
|
||
#### setCreateEndpoint(endpoint)
|
||
Устанавливает endpoint для создания элемента (POST).
|
||
|
||
```javascript
|
||
dialog.setCreateEndpoint('/api/users')
|
||
```
|
||
|
||
#### setUpdateEndpoint(endpoint)
|
||
Устанавливает endpoint для обновления элемента (PUT). Используйте `{id}` для подстановки ID элемента.
|
||
|
||
```javascript
|
||
dialog.setUpdateEndpoint('/api/users/{id}')
|
||
```
|
||
|
||
#### setGetEndpoint(endpoint)
|
||
Устанавливает endpoint для получения данных элемента (GET). Используйте `{id}` для подстановки ID элемента.
|
||
|
||
```javascript
|
||
dialog.setGetEndpoint('/api/users/{id}')
|
||
```
|
||
|
||
#### setDeleteEndpoint(endpoint)
|
||
Устанавливает endpoint для удаления элемента (DELETE). Используйте `{id}` для подстановки ID элемента.
|
||
|
||
```javascript
|
||
dialog.setDeleteEndpoint('/api/users/{id}')
|
||
```
|
||
|
||
#### openCreate(title)
|
||
Открывает диалог в режиме создания.
|
||
|
||
```javascript
|
||
dialog.openCreate('Новый пользователь')
|
||
.onSuccess(function(result) {
|
||
console.log('Создан:', result);
|
||
})
|
||
.onError(function(error) {
|
||
console.error('Ошибка:', error);
|
||
});
|
||
```
|
||
|
||
#### openUpdate(itemId, title)
|
||
Открывает диалог в режиме редактирования. Автоматически загружает данные через GET endpoint.
|
||
|
||
```javascript
|
||
dialog.openUpdate(42, 'Редактирование пользователя')
|
||
.onSuccess(function(result) {
|
||
console.log('Обновлен:', result);
|
||
})
|
||
.onDelete(function(id) {
|
||
console.log('Удален:', id);
|
||
})
|
||
.onError(function(error) {
|
||
console.error('Ошибка:', error);
|
||
});
|
||
```
|
||
|
||
#### openCreateFrom(itemId, title)
|
||
Открывает диалог в режиме создания с загруженными данными через GET endpoint.
|
||
|
||
```javascript
|
||
dialog.openUpdate(42, 'Создание копии пользователя')
|
||
.onSuccess(function(result) {
|
||
console.log('Создана копия:', result);
|
||
})
|
||
.onError(function(error) {
|
||
console.error('Ошибка:', error);
|
||
});
|
||
```
|
||
|
||
#### getConfig()
|
||
Возвращает текущую конфигурацию диалога.
|
||
|
||
```javascript
|
||
const config = dialog.getConfig();
|
||
console.log(config.fields, config.options, config.endpoints);
|
||
```
|
||
|
||
#### destroy()
|
||
Удаляет регистрацию диалога и закрывает его, если открыт.
|
||
|
||
```javascript
|
||
dialog.destroy();
|
||
```
|
||
|
||
---
|
||
|
||
### Методы API при открытом диалоге
|
||
|
||
При открытии диалога возвращается объект с методами для управления текущим экземпляром:
|
||
|
||
```javascript
|
||
const api = dialog.openCreate('Заголовок');
|
||
```
|
||
|
||
#### onSuccess(callback)
|
||
Устанавливает обработчик успешного выполнения операции.
|
||
|
||
```javascript
|
||
api.onSuccess(function(result) {
|
||
// result - данные, возвращенные сервером
|
||
console.log('Успех:', result);
|
||
});
|
||
```
|
||
|
||
#### onError(callback)
|
||
Устанавливает обработчик ошибок.
|
||
|
||
```javascript
|
||
api.onError(function(error) {
|
||
console.error('Ошибка:', error.message);
|
||
});
|
||
```
|
||
|
||
#### onDelete(callback)
|
||
Устанавливает обработчик успешного удаления (только для режима update).
|
||
|
||
```javascript
|
||
api.onDelete(function(itemId) {
|
||
console.log('Удален элемент с ID:', itemId);
|
||
});
|
||
```
|
||
|
||
#### onOpen(callback)
|
||
Устанавливает обработчик успешного открытия. Если диалог открыт
|
||
в режиме редактирования, функция будет вызвана после загрузки всех данных.
|
||
|
||
```javascript
|
||
api.onOpen(function() {
|
||
console.log('Диалог открыт и загружен');
|
||
});
|
||
```
|
||
|
||
#### setValues(values)
|
||
Устанавливает значения полей формы.
|
||
|
||
```javascript
|
||
api.setValues({
|
||
name: 'Иван',
|
||
email: 'ivan@example.com'
|
||
});
|
||
```
|
||
|
||
#### getFormData()
|
||
Возвращает текущие данные формы.
|
||
|
||
```javascript
|
||
const data = api.getFormData();
|
||
console.log(data);
|
||
```
|
||
|
||
#### close()
|
||
Закрывает диалог программно.
|
||
|
||
```javascript
|
||
api.close();
|
||
```
|
||
|
||
#### show()
|
||
Показывает диалог (если был скрыт).
|
||
|
||
```javascript
|
||
api.show();
|
||
```
|
||
|
||
---
|
||
|
||
### Глобальные методы
|
||
|
||
Работа с диалогами через глобальный объект `crud_d` по dialogId.
|
||
|
||
#### setCreateEndpoint(dialogId, endpoint)
|
||
```javascript
|
||
crud_d.setCreateEndpoint('user_form', '/api/users');
|
||
```
|
||
|
||
#### setUpdateEndpoint(dialogId, endpoint)
|
||
```javascript
|
||
crud_d.setUpdateEndpoint('user_form', '/api/users/{id}');
|
||
```
|
||
|
||
#### setGetEndpoint(dialogId, endpoint)
|
||
```javascript
|
||
crud_d.setGetEndpoint('user_form', '/api/users/{id}');
|
||
```
|
||
|
||
#### setDeleteEndpoint(dialogId, endpoint)
|
||
```javascript
|
||
crud_d.setDeleteEndpoint('user_form', '/api/users/{id}');
|
||
```
|
||
|
||
#### openCreate(dialogId, title)
|
||
```javascript
|
||
crud_d.openCreate('user_form', 'Новый пользователь')
|
||
.onSuccess(function(result) {
|
||
console.log('Создан:', result);
|
||
});
|
||
```
|
||
|
||
#### openUpdate(dialogId, itemId, title)
|
||
```javascript
|
||
crud_d.openUpdate('user_form', 42, 'Редактирование')
|
||
.onSuccess(function(result) {
|
||
console.log('Обновлен:', result);
|
||
});
|
||
```
|
||
|
||
#### openCreateFrom(dialogId, itemId, title)
|
||
```javascript
|
||
crud_d.openUpdate('user_form', 42, 'Копирование')
|
||
.onSuccess(function(result) {
|
||
console.log('Создан из объекта:', result);
|
||
});
|
||
```
|
||
|
||
#### closeDialog(dialogId)
|
||
```javascript
|
||
crud_d.closeDialog('user_form');
|
||
```
|
||
|
||
#### getRegisteredDialogs()
|
||
```javascript
|
||
const dialogs = crud_d.getRegisteredDialogs();
|
||
console.log(dialogs); // ['user_form', 'product_form']
|
||
```
|
||
|
||
#### closeAll()
|
||
```javascript
|
||
crud_d.closeAll();
|
||
```
|
||
|
||
---
|
||
|
||
## Типы полей
|
||
|
||
### text
|
||
Текстовое поле ввода.
|
||
|
||
```javascript
|
||
fieldName: ['text', {
|
||
label: 'Название поля', // string - заголовок
|
||
required: true, // boolean - обязательно для заполнения
|
||
placeholder: 'Подсказка', // string - текст-подсказка
|
||
readonly: false, // boolean - только для чтения
|
||
maxlength: 100 // number - максимальная длина
|
||
}, 'значение по умолчанию']
|
||
```
|
||
|
||
### number
|
||
Числовое поле ввода.
|
||
|
||
```javascript
|
||
fieldName: ['number', {
|
||
label: 'Цена',
|
||
required: true,
|
||
min: 0, // number - минимальное значение
|
||
max: 1000000, // number - максимальное значение
|
||
step: 0.01 // number - шаг изменения
|
||
}, 0]
|
||
```
|
||
|
||
### email
|
||
Поле для email с валидацией формата.
|
||
|
||
```javascript
|
||
fieldName: ['email', {
|
||
label: 'Email',
|
||
required: true,
|
||
placeholder: 'user@example.com'
|
||
}, '']
|
||
```
|
||
|
||
### password
|
||
Поле для пароля (скрытый ввод).
|
||
|
||
```javascript
|
||
fieldName: ['password', {
|
||
label: 'Пароль',
|
||
required: true,
|
||
placeholder: 'Введите пароль'
|
||
}, '']
|
||
```
|
||
|
||
### textarea
|
||
Многострочное текстовое поле.
|
||
|
||
```javascript
|
||
fieldName: ['textarea', {
|
||
label: 'Описание',
|
||
required: false,
|
||
rows: 5 // number - количество строк
|
||
}, '']
|
||
```
|
||
|
||
### select
|
||
Выпадающий список.
|
||
|
||
```javascript
|
||
fieldName: ['select', {
|
||
label: 'Категория',
|
||
required: true,
|
||
options: [ // array - список опций
|
||
{ value: 'electronics', label: 'Электроника' },
|
||
{ value: 'books', label: 'Книги' },
|
||
{ value: 'clothing', label: 'Одежда' }
|
||
]
|
||
}, 'electronics'] // значение по умолчанию
|
||
```
|
||
|
||
### checkbox
|
||
Флажок (чекбокс).
|
||
|
||
```javascript
|
||
fieldName: ['checkbox', {
|
||
label: 'Активен',
|
||
required: false
|
||
}, true] // значение по умолчанию
|
||
```
|
||
|
||
### date
|
||
Поле выбора даты.
|
||
|
||
```javascript
|
||
fieldName: ['date', {
|
||
label: 'Дата рождения',
|
||
required: true
|
||
}, '2024-01-01']
|
||
```
|
||
|
||
### custom
|
||
Кастомное поле на основе HTMLElement.
|
||
|
||
```javascript
|
||
const customElement = document.createElement('div');
|
||
customElement.innerHTML = `
|
||
<label class="form-label">Цвет</label>
|
||
<input type="color" class="form-control">
|
||
`;
|
||
|
||
fieldName: ['custom', customElement, '#ff0000']
|
||
```
|
||
|
||
---
|
||
|
||
## Настройки диалога
|
||
|
||
Объект options передается третьим параметром в `makeDialog`:
|
||
|
||
```javascript
|
||
{
|
||
width: 'lg', // Ширина диалога: 'sm', 'lg', 'xl' или CSS значение ('800px', '90%')
|
||
categories: { // Категории (вкладки)
|
||
'Основное': [
|
||
['name', 'price'],
|
||
['description']
|
||
],
|
||
'Дополнительно': [
|
||
['sku', 'weight'],
|
||
['dimensions']
|
||
]
|
||
}
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## Layout и категории
|
||
|
||
### Без категорий
|
||
Если категории не указаны, все поля отображаются последовательно по одному в ряду.
|
||
|
||
```javascript
|
||
crud_d.makeDialog('simple', {
|
||
name: ['text', { label: 'Имя' }, ''],
|
||
email: ['email', { label: 'Email' }, ''],
|
||
phone: ['text', { label: 'Телефон' }, '']
|
||
});
|
||
```
|
||
|
||
### Layout в категориях
|
||
Каждая категория содержит массив рядов. Ряд — это массив имен полей, которые будут в одной строке.
|
||
|
||
```javascript
|
||
categories: {
|
||
'Основная информация': [
|
||
['first_name', 'last_name'], // 2 поля в ряд
|
||
['email', 'phone'], // 2 поля в ряд
|
||
['address'] // 1 поле в ряд
|
||
],
|
||
'Дополнительно': [
|
||
['birth_date', 'gender'], // 2 поля в ряд
|
||
['notes'] // 1 поле в ряд
|
||
]
|
||
}
|
||
```
|
||
|
||
### Автоматическая категория "Дополнительно"
|
||
- Если указана только одна категория, все поля, не попавшие в неё, автоматически добавляются во вкладку "Дополнительно"
|
||
- Если категорий несколько, нераспределенные поля также добавляются в "Дополнительно"
|
||
- Если категории не указаны вообще, все поля идут подряд без вкладок
|
||
|
||
### Ширина колонок в ряду
|
||
Автоматически рассчитывается на основе количества полей:
|
||
- 1 поле: `col-12` (полная ширина)
|
||
- 2 поля: `col-md-6` (по половине)
|
||
- 3 поля: `col-md-4` (по трети)
|
||
- 4 поля: `col-md-3` (по четверти)
|
||
|
||
---
|
||
|
||
## Примеры использования
|
||
|
||
### Простой диалог создания
|
||
|
||
```javascript
|
||
const dialog = crud_d.makeDialog('simple_user', {
|
||
name: ['text', { label: 'Имя', required: true }, ''],
|
||
email: ['email', { label: 'Email', required: true }, '']
|
||
});
|
||
|
||
dialog.setCreateEndpoint('/api/users');
|
||
|
||
// Открытие
|
||
dialog.openCreate('Новый пользователь')
|
||
.onSuccess(function(result) {
|
||
alert('Пользователь создан!');
|
||
});
|
||
```
|
||
|
||
### Диалог с обновлением и удалением
|
||
|
||
```javascript
|
||
const dialog = crud_d.makeDialog('user_edit', {
|
||
name: ['text', { label: 'Имя', required: true }, ''],
|
||
email: ['email', { label: 'Email', required: true }, ''],
|
||
role: ['select', {
|
||
label: 'Роль',
|
||
options: [
|
||
{ value: 'user', label: 'Пользователь' },
|
||
{ value: 'admin', label: 'Администратор' }
|
||
]
|
||
}, 'user']
|
||
});
|
||
|
||
dialog.setGetEndpoint('/api/users/{id}');
|
||
dialog.setUpdateEndpoint('/api/users/{id}');
|
||
dialog.setDeleteEndpoint('/api/users/{id}');
|
||
|
||
// Открытие на редактирование
|
||
dialog.openUpdate(42, 'Редактирование пользователя')
|
||
.onSuccess(function(result) {
|
||
console.log('Обновлен:', result);
|
||
// Обновить список пользователей
|
||
})
|
||
.onDelete(function(id) {
|
||
console.log('Удален:', id);
|
||
// Удалить из списка
|
||
})
|
||
.onError(function(error) {
|
||
alert('Ошибка: ' + error.message);
|
||
});
|
||
```
|
||
|
||
### Сложный диалог с категориями
|
||
|
||
```javascript
|
||
const dialog = crud_d.makeDialog('product_form', {
|
||
name: ['text', { label: 'Название', required: true }, ''],
|
||
price: ['number', { label: 'Цена', required: true, min: 0 }, 0],
|
||
quantity: ['number', { label: 'Количество', min: 0 }, 0],
|
||
category: ['select', {
|
||
label: 'Категория',
|
||
options: [
|
||
{ value: 'electronics', label: 'Электроника' },
|
||
{ value: 'clothing', label: 'Одежда' }
|
||
]
|
||
}, 'electronics'],
|
||
description: ['textarea', { label: 'Описание', rows: 5 }, ''],
|
||
weight: ['number', { label: 'Вес (кг)', step: 0.1 }, null],
|
||
dimensions: ['text', { label: 'Размеры' }, ''],
|
||
manufacturer: ['text', { label: 'Производитель' }, ''],
|
||
in_stock: ['checkbox', { label: 'В наличии' }, true]
|
||
}, {
|
||
width: 'lg',
|
||
categories: {
|
||
'Основное': [
|
||
['name'],
|
||
['price', 'quantity', 'in_stock'],
|
||
['category'],
|
||
['description']
|
||
],
|
||
'Характеристики': [
|
||
['weight', 'dimensions'],
|
||
['manufacturer']
|
||
]
|
||
}
|
||
});
|
||
|
||
dialog.setCreateEndpoint('/api/products');
|
||
dialog.setGetEndpoint('/api/products/{id}');
|
||
dialog.setUpdateEndpoint('/api/products/{id}');
|
||
dialog.setDeleteEndpoint('/api/products/{id}');
|
||
```
|
||
|
||
### Глобальное управление
|
||
|
||
```javascript
|
||
// Регистрируем диалог
|
||
crud_d.makeDialog('quick_user', {
|
||
name: ['text', { label: 'Имя' }, ''],
|
||
email: ['email', { label: 'Email' }, '']
|
||
});
|
||
|
||
// Устанавливаем эндпоинты глобально
|
||
crud_d.setCreateEndpoint('quick_user', '/api/users');
|
||
crud_d.setGetEndpoint('quick_user', '/api/users/{id}');
|
||
|
||
// Открываем из любого места
|
||
crud_d.openCreate('quick_user', 'Быстрое создание')
|
||
.onSuccess(function(result) {
|
||
console.log('Готово!', result);
|
||
});
|
||
|
||
// Закрываем при необходимости
|
||
crud_d.closeDialog('quick_user');
|
||
|
||
// Закрыть все открытые диалоги
|
||
crud_d.closeAll();
|
||
```
|
||
|
||
### Кастомное поле
|
||
|
||
```javascript
|
||
// Создаем кастомный элемент для выбора цвета
|
||
const colorPicker = document.createElement('div');
|
||
colorPicker.className = 'mb-3';
|
||
colorPicker.innerHTML = `
|
||
<label class="form-label">Цвет товара</label>
|
||
<div class="input-group">
|
||
<input type="color" class="form-control form-control-color"
|
||
value="#563d7c" title="Выберите цвет">
|
||
<input type="text" class="form-control" value="#563d7c">
|
||
</div>
|
||
`;
|
||
|
||
// Синхронизация инпутов
|
||
const [colorInput, textInput] = colorPicker.querySelectorAll('input');
|
||
colorInput.addEventListener('input', () => textInput.value = colorInput.value);
|
||
textInput.addEventListener('input', () => colorInput.value = textInput.value);
|
||
|
||
const dialog = crud_d.makeDialog('custom_example', {
|
||
name: ['text', { label: 'Название' }, ''],
|
||
color: ['custom', colorPicker, '#563d7c']
|
||
});
|
||
|
||
dialog.setCreateEndpoint('/api/products');
|
||
dialog.openCreate('Товар с цветом');
|
||
```
|
||
|
||
### Установка значений по умолчанию
|
||
|
||
```javascript
|
||
dialog.openCreate('Новый заказ')
|
||
.setValues({
|
||
priority: 'high',
|
||
status: 'pending',
|
||
date: new Date().toISOString().split('T')[0]
|
||
})
|
||
.onSuccess(function(result) {
|
||
console.log('Заказ создан:', result);
|
||
});
|
||
```
|
||
|
||
---
|
||
|
||
## События и колбэки
|
||
|
||
### Порядок выполнения
|
||
|
||
1. **Создание (create):**
|
||
- Валидация формы
|
||
- POST запрос на create endpoint
|
||
- `onSuccess` при успехе
|
||
- `onError` при ошибке
|
||
- Автоматическое закрытие диалога при успехе
|
||
|
||
2. **Обновление (update):**
|
||
- GET запрос на get endpoint для загрузки данных
|
||
- Заполнение формы полученными данными
|
||
- При сохранении: валидация → PUT запрос → `onSuccess`/`onError`
|
||
- При удалении: подтверждение → DELETE запрос → `onDelete`/`onError`
|
||
- Автоматическое закрытие диалога при успехе
|
||
|
||
3. **Создание из существующего элемента (create from):**
|
||
- GET запрос на get endpoint для загрузки данных
|
||
- Заполнение формы полученными данными
|
||
- Валидация формы
|
||
- POST запрос на create endpoint
|
||
- `onSuccess` при успехе
|
||
- `onError` при ошибке
|
||
- Автоматическое закрытие диалога при успехе
|
||
|
||
### Цепочка методов
|
||
|
||
Все методы настройки возвращают `this`, что позволяет использовать цепочку вызовов:
|
||
|
||
```javascript
|
||
dialog
|
||
.setCreateEndpoint('/api/users')
|
||
.setGetEndpoint('/api/users/{id}')
|
||
.openCreate('Пользователь')
|
||
.onSuccess(handleSuccess)
|
||
.onError(handleError);
|
||
```
|
||
|
||
---
|
||
|
||
## Обработка ошибок
|
||
|
||
Библиотека автоматически обрабатывает HTTP ошибки и отображает их в диалоге:
|
||
|
||
- Ошибки валидации отображаются в alert-блоке внутри диалога
|
||
- Ошибки сети/сервера отображаются там же
|
||
- Все ошибки также передаются в `onError` колбэк
|
||
|
||
```javascript
|
||
dialog.openCreate('Форма')
|
||
.onError(function(error) {
|
||
// error.message содержит текст ошибки
|
||
if (error.message.includes('422')) {
|
||
// Ошибка валидации на сервере
|
||
} else if (error.message.includes('500')) {
|
||
// Ошибка сервера
|
||
}
|
||
});
|
||
```
|
||
|
||
---
|
||
|
||
## Примечания
|
||
|
||
- Все эндпоинты поддерживают подстановку `{id}` в URL
|
||
- При закрытии диалога через `data-bs-dismiss="modal"` он автоматически очищается
|
||
- Одновременно может быть открыт только один экземпляр диалога с одинаковым dialogId
|
||
- Библиотека требует Bootstrap 5 и библиотеку dom.js
|
||
- Все HTTP запросы отправляются с заголовком `Content-Type: application/json` |