Files
michaelschiemer/docs/javascript/action-handler-guide.md
2025-11-24 21:28:25 +01:00

12 KiB

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

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

<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

// 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

// 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

// 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

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

<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

const myHandler = {
    loadingTexts: {
        start: 'Starting...',
        stop: 'Stopping...',
        delete: 'Deleting...'
    }
};

Element-basierte Loading-Texte

<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

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

<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

// 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

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

<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)

<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:

// Gut
const handler = new ActionHandler('.docker-containers-component');

// Schlecht
const handler = new ActionHandler('body');

2. Handler wiederverwenden

Erstelle wiederverwendbare Handler für ähnliche Actions:

// Gut: Wiederverwendbarer Handler
const userHandler = {
    urlTemplate: '/api/users/{id}/{action}',
    // ...
};

// Schlecht: Inline-Konfiguration für jede Action

3. Konsistente Namenskonventionen

Verwende konsistente Action-Namen:

// 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:

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

<button 
    data-action="start"
    data-action-handler="docker-container"
    data-action-param-id="{{containerId}}"
>
    Start
</button>

Bulk Operations

<button 
    data-action="delete"
    data-action-handler="bulk-operations"
    data-action-param-entity="users"
>
    Delete Selected
</button>

API-Referenz

ActionHandler Konstruktor

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

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

<div class="user-actions">
    <button 
        data-action="delete"
        data-action-handler="user-handler"
        data-action-param-id="123"
    >
        Delete User
    </button>
</div>
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

<button 
    data-action="custom"
    data-action-url="/api/custom-endpoint"
    data-action-method="PUT"
>
    Custom Action
</button>

Beispiel 3: Action mit mehreren Parametern

<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>
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