Hotwire — Použití
HTML atributy — přehled
| Atribut | Trigger | Co dělá |
|---|---|---|
data-content="url" |
click | GET → HTML → inject #content + URL change |
data-json="url" |
click | POST/DELETE → JSON → message + callback |
data-method="DELETE" |
— | HTTP method pro data-json (default: POST) |
data-confirm="zpráva" |
— | Potvrzovací dialog před akcí |
data-after="fnName" |
— | Callback window[fnName](response, el) po akci |
data-timeout="ms" |
— | Vlastní timeout v milisekundách |
data-unlimited |
— | Bez timeoutu (čeká neomezeně dlouho) |
data-filter |
submit / change | Filter form → POST + reset stránkování |
data-form="pageName" |
submit | CRUD form (save / close / back logika) |
data-content — navigace
Načte HTML z URL a injektuje do #content. Aktualizuje adresu v prohlížeči.
<a href="/ADMIN/cs/articles" data-content="/ADMIN/cs/articles">
Seznam článků
</a>
Atribut href slouží jako fallback pokud JS není k dispozici. Hodnota data-content je URL, která se skutečně načte.
data-json — JSON akce
Odešle požadavek a zpracuje JSON odpověď. Zobrazí zprávu, volá callback.
<!-- Smazání záznamu -->
<a href="#"
data-json="/ADMIN/cs/articles/123"
data-method="DELETE"
data-confirm="Opravdu chcete smazat: Název článku?"
title="Odstranit">
Odstranit
</a>
<!-- Přepnutí stavu (bez confirm) -->
<button
data-json="/ADMIN/cs/articles/123/toggle"
data-method="POST">
Přepnout stav
</button>
S callbackem
<button
data-json="/ADMIN/cs/cubeTypes/5/toggle"
data-after="toggleActiveIcon">
Aktivovat
</button>
// Callback musí být registrován na window
window.toggleActiveIcon = (response, el) => {
const icon = el.closest('tr').querySelector('.status-icon');
icon.classList.toggle('active', response.data.active);
};
S vlastním timeoutem
<!-- DeepL překlad — 30 sekund -->
<button
data-json="/ADMIN/cs/translations/deepl/translate"
data-timeout="30000">
Přeložit (DeepL)
</button>
<!-- AI analýza — bez timeoutu -->
<button
data-json="/ADMIN/cs/openai/analyze"
data-unlimited>
Analyzovat (AI)
</button>
data-filter — filtrovací formulář
Form se odešle AJAXem a výsledek nahradí #content. Stránkování se při filtraci resetuje.
<form data-filter action="/ADMIN/cs/articles">
<select name="status">
<option value="">Vše</option>
<option value="published">Publikováno</option>
<option value="draft">Koncept</option>
</select>
<input type="submit" value="Filtrovat">
</form>
Select se odešle automaticky při změně (bez kliknutí na submit).
data-form — CRUD formulář
Komplexní formulář pro vytváření a editaci záznamů. Rozlišuje tři tlačítka podle atributu name:
Tlačítko name |
Chování po úspěchu |
|---|---|
save |
Zůstane na formuláři, updatne ID pokud nový záznam |
close |
Přejde na list (data-list nebo výchozí URL modulu) |
back |
Přejde na list bez uložení |
<form data-form="articles" data-list="articles">
<input type="hidden" name="id" value="123">
<input type="text" name="title" required>
<textarea name="content"></textarea>
<div class="form_errors"></div>
<button name="save" type="submit">Uložit</button>
<button name="close" type="submit">Uložit a zavřít</button>
<button name="back" type="submit">Zpět bez uložení</button>
</form>
URL logika:
- Nový záznam (bez id): POST na /ADMIN/{lang}/articles
- Existující (s id): POST na /ADMIN/{lang}/articles/123
Validační chyby ze serveru se zobrazí přes div.form_errors uvnitř formuláře.
Kombinace atributů v praxi
Typické akce v admin listingu:
<!-- Editace — načte formulář do #content -->
<a href="#" data-content="/ADMIN/cs/articles/123/edit" title="Upravit">
Upravit
</a>
<!-- Smazání — DELETE s potvrzením -->
<a href="#"
data-json="/ADMIN/cs/articles/123"
data-method="DELETE"
data-confirm="Opravdu chcete smazat: Název článku?"
title="Odstranit">
Odstranit
</a>
<!-- Přidání nového záznamu -->
<a href="#" data-content="/ADMIN/cs/articles/new" title="Přidat">
Přidat
</a>
PHP — serverová strana
HTML odpověď (data-content)
Controller detekuje zda jde o AJAX fetch podle hlavičky X-Requested-With. Podle toho vrátí buď samotný HTML fragment (pro inject do #content), nebo celou stránku (pro přímý přístup v prohlížeči):
$isFetch = has_header($request, 'X-REQUESTED-WITH');
return $isFetch
? html($html) // fragment → inject do #content
: Html::wrap($html); // celá stránka → přímý přístup
Stejná URL tedy funguje pro obě situace — hotwire AJAX i přímý reload prohlížeče.
JSON odpověď (data-json)
Helpery json() a translatedJson() z core/Http/Response.php:
// Úspěch — zpráva se přeloží automaticky dle LANG
return translatedJson(true, 'Page deleted');
// Chyba
return translatedJson(false, 'Delete failed');
// S daty
return translatedJson(true, 'Page created', ['id' => $pageId]);
Výsledný JSON má vždy tvar:
{ "status": "ok", "message": "Stránka byla smazána." }
{ "status": "error", "message": "Smazání selhalo." }
{ "status": "ok", "message": "Stránka vytvořena.", "data": { "id": 42 } }
Redirect po akci
Po úspěšné akci server může říct JS kam přejít přes redirectUrl v data:
// Soft redirect — hotwire načte HTML do #content a změní URL
return translatedJson(true, 'Page deleted', [
'redirectUrl' => '/ADMIN/' . LANG . '/pages',
]);
// Hard redirect — plný reload prohlížeče (window.location.href)
return translatedJson(true, 'Page duplicated', [
'redirectUrl' => '/ADMIN/' . LANG . '/pages/' . $newPageId,
'redirectType' => 'hard',
]);
Soft redirect (výchozí): JS načte redirectUrl jako AJAX, injektuje HTML do #content a aktualizuje URL v prohlížeči — bez reloadu stránky.
Hard redirect: JS provede window.location.href = redirectUrl — plný reload. Použij když nová stránka vyžaduje jiný stav (jiná JS inicializace, session změna apod.).
Zpracování na straně JS
Celý flow po přijetí JSON odpovědi v fetch_json.js:
JSON přijat
↓
json.message → zobraz message (success / error)
↓
json.data.redirectUrl?
├── redirectType === 'hard' → window.location.href = redirectUrl
└── (default soft) → fetch HTML → inject #content → pushState
↓
el.dataset.after → zavolej window[fnName](json, el)
Edge cases
Vypršení session
Pokud server vrátí login formulář místo očekávané odpovědi (session expirovala), request.js to automaticky detekuje a přesměruje na /ADMIN/{lang}/login.
Potvrzovací dialog
data-confirm použije nativní window.confirm(). Pokud uživatel klikne Storno, požadavek se vůbec neodešle.
Timeout
Výchozí timeout je nastaven v config.js (config.requestTimeout). Lze přepsat per-element přes data-timeout="ms" nebo úplně zrušit přes data-unlimited.