fix: DockerSecretsResolver - don't normalize absolute paths like /var/www/html/...
Some checks failed
Deploy Application / deploy (push) Has been cancelled
Some checks failed
Deploy Application / deploy (push) Has been cancelled
This commit is contained in:
523
docs/javascript/action-handler-guide.md
Normal file
523
docs/javascript/action-handler-guide.md
Normal file
@@ -0,0 +1,523 @@
|
||||
# ActionHandler Guide
|
||||
|
||||
## Übersicht
|
||||
|
||||
Das `ActionHandler` Modul bietet ein wiederverwendbares Pattern für Event-Delegation-basiertes Action-Handling mit automatischem CSRF-Token-Handling, Loading States, Bestätigungen und Toast-Integration.
|
||||
|
||||
## Konzepte
|
||||
|
||||
### Event Delegation
|
||||
|
||||
Statt jedem Button einen eigenen Event Listener zuzuweisen, verwendet ActionHandler Event Delegation: Ein einziger Event Listener auf dem Container-Element behandelt alle Actions.
|
||||
|
||||
**Vorteile:**
|
||||
- Bessere Performance (weniger Event Listener)
|
||||
- Funktioniert mit dynamisch hinzugefügten Elementen
|
||||
- Einfacheres Code-Management
|
||||
|
||||
### Handler-Registry
|
||||
|
||||
Handler sind vorkonfigurierte Action-Patterns, die wiederverwendet werden können. Sie definieren:
|
||||
- URL-Templates
|
||||
- Bestätigungs-Nachrichten
|
||||
- Loading-Texte
|
||||
- Success/Error-Messages
|
||||
|
||||
### URL-Templates
|
||||
|
||||
URL-Templates verwenden Platzhalter, die zur Laufzeit ersetzt werden:
|
||||
- `{action}` - Der Action-Name
|
||||
- `{id}` - ID aus `data-action-param-id`
|
||||
- `{param:name}` - Parameter aus `data-action-param-{name}`
|
||||
- `{data:name}` - Wert aus `data-{name}` Attribut
|
||||
|
||||
## Basis-Verwendung
|
||||
|
||||
### 1. ActionHandler initialisieren
|
||||
|
||||
```javascript
|
||||
import { ActionHandler } from '/assets/js/modules/common/ActionHandler.js';
|
||||
import { dockerContainerHandler } from '/assets/js/modules/common/ActionHandlers.js';
|
||||
|
||||
const handler = new ActionHandler('.my-container', {
|
||||
csrfTokenSelector: '[data-live-component]',
|
||||
toastHandler: showToast,
|
||||
refreshHandler: refreshComponent,
|
||||
autoRefresh: true
|
||||
});
|
||||
|
||||
// Handler registrieren
|
||||
handler.registerHandler('docker-container', dockerContainerHandler);
|
||||
```
|
||||
|
||||
### 2. HTML mit data-* Attributen
|
||||
|
||||
```html
|
||||
<div class="my-container">
|
||||
<button
|
||||
data-action="start"
|
||||
data-action-handler="docker-container"
|
||||
data-action-param-id="container-123"
|
||||
>
|
||||
Start Container
|
||||
</button>
|
||||
|
||||
<button
|
||||
data-action="stop"
|
||||
data-action-handler="docker-container"
|
||||
data-action-param-id="container-123"
|
||||
>
|
||||
Stop Container
|
||||
</button>
|
||||
</div>
|
||||
```
|
||||
|
||||
## URL-Templates
|
||||
|
||||
### Einfaches Template
|
||||
|
||||
```javascript
|
||||
// Handler-Konfiguration
|
||||
const myHandler = {
|
||||
urlTemplate: '/api/users/{id}/{action}',
|
||||
method: 'POST'
|
||||
};
|
||||
|
||||
// HTML
|
||||
<button
|
||||
data-action="delete"
|
||||
data-action-handler="my-handler"
|
||||
data-action-param-id="123"
|
||||
>
|
||||
Delete User
|
||||
</button>
|
||||
|
||||
// Wird zu: /api/users/123/delete
|
||||
```
|
||||
|
||||
### Template mit mehreren Parametern
|
||||
|
||||
```javascript
|
||||
// Handler-Konfiguration
|
||||
const myHandler = {
|
||||
urlTemplate: '/api/{entity}/{id}/{action}',
|
||||
method: 'POST'
|
||||
};
|
||||
|
||||
// HTML
|
||||
<button
|
||||
data-action="update"
|
||||
data-action-handler="my-handler"
|
||||
data-action-param-entity="users"
|
||||
data-action-param-id="123"
|
||||
>
|
||||
Update User
|
||||
</button>
|
||||
|
||||
// Wird zu: /api/users/123/update
|
||||
```
|
||||
|
||||
### Template mit {data:*} Platzhaltern
|
||||
|
||||
```javascript
|
||||
// Handler-Konfiguration
|
||||
const myHandler = {
|
||||
urlTemplate: '/api/{data:entity}/{data:id}/{action}',
|
||||
method: 'POST'
|
||||
};
|
||||
|
||||
// HTML
|
||||
<button
|
||||
data-action="delete"
|
||||
data-action-handler="my-handler"
|
||||
data-entity="users"
|
||||
data-id="123"
|
||||
>
|
||||
Delete
|
||||
</button>
|
||||
|
||||
// Wird zu: /api/users/123/delete
|
||||
```
|
||||
|
||||
## CSRF-Token Handling
|
||||
|
||||
ActionHandler extrahiert CSRF-Token automatisch aus verschiedenen Quellen (in Reihenfolge):
|
||||
|
||||
1. `data-csrf-token` Attribut des Action-Elements
|
||||
2. `data-csrf-token` Attribut des nächstgelegenen `[data-live-component]` Elements
|
||||
3. `data-csrf-token` Attribut des Containers
|
||||
4. Meta-Tag `<meta name="csrf-token">`
|
||||
5. Fallback: Leerer String
|
||||
|
||||
Das Token wird automatisch hinzugefügt:
|
||||
- Header: `X-CSRF-Token`
|
||||
- Body: `_token` und `_form_id`
|
||||
|
||||
## Bestätigungen
|
||||
|
||||
### Handler-basierte Bestätigungen
|
||||
|
||||
```javascript
|
||||
const myHandler = {
|
||||
urlTemplate: '/api/users/{id}/{action}',
|
||||
confirmations: {
|
||||
delete: 'Are you sure you want to delete this user?',
|
||||
update: 'Are you sure you want to update this user?'
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
### Element-basierte Bestätigungen
|
||||
|
||||
```html
|
||||
<button
|
||||
data-action="delete"
|
||||
data-action-confirm="Are you sure?"
|
||||
data-action-handler="my-handler"
|
||||
data-action-param-id="123"
|
||||
>
|
||||
Delete
|
||||
</button>
|
||||
```
|
||||
|
||||
## Loading States
|
||||
|
||||
### Handler-basierte Loading-Texte
|
||||
|
||||
```javascript
|
||||
const myHandler = {
|
||||
loadingTexts: {
|
||||
start: 'Starting...',
|
||||
stop: 'Stopping...',
|
||||
delete: 'Deleting...'
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
### Element-basierte Loading-Texte
|
||||
|
||||
```html
|
||||
<button
|
||||
data-action="delete"
|
||||
data-action-loading-text="Deleting user..."
|
||||
data-action-handler="my-handler"
|
||||
data-action-param-id="123"
|
||||
>
|
||||
Delete
|
||||
</button>
|
||||
```
|
||||
|
||||
## Toast-Integration
|
||||
|
||||
### Handler-basierte Toast-Messages
|
||||
|
||||
```javascript
|
||||
const myHandler = {
|
||||
successMessages: {
|
||||
delete: 'User deleted successfully',
|
||||
update: 'User updated successfully'
|
||||
},
|
||||
errorMessages: {
|
||||
delete: 'Failed to delete user',
|
||||
update: 'Failed to update user'
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
### Element-basierte Toast-Messages
|
||||
|
||||
```html
|
||||
<button
|
||||
data-action="delete"
|
||||
data-action-success-toast="User deleted!"
|
||||
data-action-error-toast="Failed to delete user"
|
||||
data-action-handler="my-handler"
|
||||
data-action-param-id="123"
|
||||
>
|
||||
Delete
|
||||
</button>
|
||||
```
|
||||
|
||||
## Custom Handler erstellen
|
||||
|
||||
### 1. Handler-Konfiguration erstellen
|
||||
|
||||
```javascript
|
||||
// resources/js/modules/common/ActionHandlers.js
|
||||
export const myCustomHandler = {
|
||||
urlTemplate: '/api/{entity}/{id}/{action}',
|
||||
method: 'POST',
|
||||
confirmations: {
|
||||
delete: 'Are you sure?'
|
||||
},
|
||||
loadingTexts: {
|
||||
delete: 'Deleting...',
|
||||
update: 'Updating...'
|
||||
},
|
||||
successMessages: {
|
||||
delete: 'Deleted successfully',
|
||||
update: 'Updated successfully'
|
||||
},
|
||||
errorMessages: {
|
||||
delete: 'Failed to delete',
|
||||
update: 'Failed to update'
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
### 2. Handler registrieren
|
||||
|
||||
```javascript
|
||||
import { ActionHandler } from '/assets/js/modules/common/ActionHandler.js';
|
||||
import { myCustomHandler } from '/assets/js/modules/common/ActionHandlers.js';
|
||||
|
||||
const handler = new ActionHandler('.my-container');
|
||||
handler.registerHandler('my-handler', myCustomHandler);
|
||||
```
|
||||
|
||||
### 3. Handler verwenden
|
||||
|
||||
```html
|
||||
<button
|
||||
data-action="delete"
|
||||
data-action-handler="my-handler"
|
||||
data-action-param-entity="users"
|
||||
data-action-param-id="123"
|
||||
>
|
||||
Delete User
|
||||
</button>
|
||||
```
|
||||
|
||||
## Spezielle Action-Typen
|
||||
|
||||
### Window-Actions (öffnen in neuem Tab)
|
||||
|
||||
```html
|
||||
<button
|
||||
data-action="logs"
|
||||
data-action-type="window"
|
||||
data-action-url="/api/containers/{id}/logs"
|
||||
data-action-param-id="123"
|
||||
>
|
||||
View Logs
|
||||
</button>
|
||||
```
|
||||
|
||||
## Best Practices
|
||||
|
||||
### 1. Container-Selektor verwenden
|
||||
|
||||
Verwende einen spezifischen Container-Selektor, um Konflikte zu vermeiden:
|
||||
|
||||
```javascript
|
||||
// Gut
|
||||
const handler = new ActionHandler('.docker-containers-component');
|
||||
|
||||
// Schlecht
|
||||
const handler = new ActionHandler('body');
|
||||
```
|
||||
|
||||
### 2. Handler wiederverwenden
|
||||
|
||||
Erstelle wiederverwendbare Handler für ähnliche Actions:
|
||||
|
||||
```javascript
|
||||
// Gut: Wiederverwendbarer Handler
|
||||
const userHandler = {
|
||||
urlTemplate: '/api/users/{id}/{action}',
|
||||
// ...
|
||||
};
|
||||
|
||||
// Schlecht: Inline-Konfiguration für jede Action
|
||||
```
|
||||
|
||||
### 3. Konsistente Namenskonventionen
|
||||
|
||||
Verwende konsistente Action-Namen:
|
||||
|
||||
```javascript
|
||||
// Gut: RESTful Actions
|
||||
start, stop, restart, delete, update, create
|
||||
|
||||
// Schlecht: Inkonsistente Namen
|
||||
startContainer, stopContainer, deleteUser, updateUserData
|
||||
```
|
||||
|
||||
### 4. Error Handling
|
||||
|
||||
ActionHandler behandelt Errors automatisch, aber du kannst Custom Error-Handling hinzufügen:
|
||||
|
||||
```javascript
|
||||
const handler = new ActionHandler('.container', {
|
||||
toastHandler: (message, type) => {
|
||||
if (type === 'error') {
|
||||
// Custom Error-Logging
|
||||
console.error('[ActionHandler]', message);
|
||||
}
|
||||
// Standard Toast
|
||||
showToast(message, type);
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
## Integration in bestehende Komponenten
|
||||
|
||||
### Docker Container Actions
|
||||
|
||||
```html
|
||||
<button
|
||||
data-action="start"
|
||||
data-action-handler="docker-container"
|
||||
data-action-param-id="{{containerId}}"
|
||||
>
|
||||
Start
|
||||
</button>
|
||||
```
|
||||
|
||||
### Bulk Operations
|
||||
|
||||
```html
|
||||
<button
|
||||
data-action="delete"
|
||||
data-action-handler="bulk-operations"
|
||||
data-action-param-entity="users"
|
||||
>
|
||||
Delete Selected
|
||||
</button>
|
||||
```
|
||||
|
||||
## API-Referenz
|
||||
|
||||
### ActionHandler Konstruktor
|
||||
|
||||
```javascript
|
||||
new ActionHandler(containerSelector, options)
|
||||
```
|
||||
|
||||
**Parameter:**
|
||||
- `containerSelector` (string): CSS-Selektor für Container-Element
|
||||
- `options` (object): Konfigurationsoptionen
|
||||
- `csrfTokenSelector` (string): Selektor für CSRF-Token Element
|
||||
- `toastHandler` (function): Funktion für Toast-Anzeige
|
||||
- `confirmationHandler` (function): Funktion für Bestätigungen
|
||||
- `loadingTexts` (object): Mapping von Action-Namen zu Loading-Texten
|
||||
- `autoRefresh` (boolean): Automatisches Refresh nach erfolgreicher Action
|
||||
- `refreshHandler` (function): Funktion für Refresh
|
||||
|
||||
### registerHandler
|
||||
|
||||
```javascript
|
||||
handler.registerHandler(name, config)
|
||||
```
|
||||
|
||||
**Parameter:**
|
||||
- `name` (string): Handler-Name
|
||||
- `config` (object): Handler-Konfiguration
|
||||
- `urlTemplate` (string): URL-Template mit Platzhaltern
|
||||
- `method` (string): HTTP-Methode (default: POST)
|
||||
- `confirmations` (object): Bestätigungs-Nachrichten pro Action
|
||||
- `loadingTexts` (object): Loading-Texte pro Action
|
||||
- `successMessages` (object): Success-Messages pro Action
|
||||
- `errorMessages` (object): Error-Messages pro Action
|
||||
|
||||
### HTML-Attribute
|
||||
|
||||
- `data-action`: Action-Name (erforderlich)
|
||||
- `data-action-handler`: Handler-Name (optional)
|
||||
- `data-action-url`: Direkte URL (optional, überschreibt Template)
|
||||
- `data-action-method`: HTTP-Methode (optional, default: POST)
|
||||
- `data-action-confirm`: Bestätigungs-Nachricht (optional)
|
||||
- `data-action-loading-text`: Loading-Text (optional)
|
||||
- `data-action-success-toast`: Success-Message (optional)
|
||||
- `data-action-error-toast`: Error-Message (optional)
|
||||
- `data-action-type`: Action-Typ (optional, z.B. "window")
|
||||
- `data-action-param-*`: Parameter für URL-Template (optional)
|
||||
|
||||
## Beispiele
|
||||
|
||||
### Beispiel 1: Einfache Action
|
||||
|
||||
```html
|
||||
<div class="user-actions">
|
||||
<button
|
||||
data-action="delete"
|
||||
data-action-handler="user-handler"
|
||||
data-action-param-id="123"
|
||||
>
|
||||
Delete User
|
||||
</button>
|
||||
</div>
|
||||
```
|
||||
|
||||
```javascript
|
||||
import { ActionHandler } from '/assets/js/modules/common/ActionHandler.js';
|
||||
|
||||
const handler = new ActionHandler('.user-actions');
|
||||
handler.registerHandler('user-handler', {
|
||||
urlTemplate: '/api/users/{id}/{action}',
|
||||
confirmations: {
|
||||
delete: 'Are you sure?'
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
### Beispiel 2: Action mit Custom URL
|
||||
|
||||
```html
|
||||
<button
|
||||
data-action="custom"
|
||||
data-action-url="/api/custom-endpoint"
|
||||
data-action-method="PUT"
|
||||
>
|
||||
Custom Action
|
||||
</button>
|
||||
```
|
||||
|
||||
### Beispiel 3: Action mit mehreren Parametern
|
||||
|
||||
```html
|
||||
<button
|
||||
data-action="transfer"
|
||||
data-action-handler="transfer-handler"
|
||||
data-action-param-from="account-1"
|
||||
data-action-param-to="account-2"
|
||||
data-action-param-amount="100"
|
||||
>
|
||||
Transfer
|
||||
</button>
|
||||
```
|
||||
|
||||
```javascript
|
||||
handler.registerHandler('transfer-handler', {
|
||||
urlTemplate: '/api/transfer/{param:from}/{param:to}/{param:amount}',
|
||||
method: 'POST'
|
||||
});
|
||||
```
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Actions werden nicht ausgeführt
|
||||
|
||||
1. Prüfe, ob Container-Selektor korrekt ist
|
||||
2. Prüfe, ob `data-action` Attribut vorhanden ist
|
||||
3. Prüfe Browser-Konsole für Warnungen
|
||||
4. Prüfe, ob Handler registriert ist
|
||||
|
||||
### CSRF-Token-Fehler
|
||||
|
||||
1. Prüfe, ob CSRF-Token-Element vorhanden ist
|
||||
2. Prüfe, ob `csrfTokenSelector` korrekt ist
|
||||
3. Prüfe, ob Token im HTML vorhanden ist
|
||||
|
||||
### URL-Template wird nicht ersetzt
|
||||
|
||||
1. Prüfe, ob Platzhalter korrekt geschrieben sind (`{id}`, nicht `{ id }`)
|
||||
2. Prüfe, ob Parameter-Attribute vorhanden sind (`data-action-param-id`)
|
||||
3. Prüfe Browser-Konsole für Warnungen
|
||||
|
||||
### Toast wird nicht angezeigt
|
||||
|
||||
1. Prüfe, ob `toastHandler` konfiguriert ist
|
||||
2. Prüfe, ob `LiveComponentUIHelper` verfügbar ist
|
||||
3. Prüfe Browser-Konsole für Fehler
|
||||
|
||||
Reference in New Issue
Block a user