CRUD_Dialogs/README.md

735 lines
23 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Документация библиотеки 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`