Skip to content

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.