seaworn / symfony-htmx-bundle
Htmx integration for Symfony
Installs: 14
Dependents: 0
Suggesters: 0
Security: 0
Stars: 3
Watchers: 2
Forks: 1
Open Issues: 0
Type:symfony-bundle
Requires
- php: >=8.0
- ext-json: *
- symfony/framework-bundle: 6.*
- symfony/twig-bundle: 6.*
Requires (Dev)
This package is auto-updated.
Last update: 2024-09-10 23:50:26 UTC
README
Htmx integration for Symfony
- Symfony >=6.0
- PHP >=8.0
Install
$ composer require seaworn/symfony-htmx-bundle
Setup
Add the following snippet to the front controller. This will allow Symfony to use a custom Request class
/* public/index.php */ \Symfony\Component\HttpFoundation\Request::setFactory(function (...$args) { return new \Seaworn\HtmxBundle\Request\HtmxRequest(...$args); });
Usage
Extend your controller from Seaworn\HtmxBundle\Controller\AbstractController
or use the Seaworn\HtmxBundle\Controller\HtmxControllerTrait
trait
// ... use Seaworn\HtmxBundle\Request\HtmxRequest; use Seaworn\HtmxBundle\Response\HtmxResponse; class HelloController extends \Seaworn\HtmxBundle\Controller\AbstractController{} // or class HelloController extends \Symfony\Bundle\FrameworkBundle\Controller\AbstractController { use \Seaworn\HtmxBundle\Controller\HtmxControllerTrait; }
Request headers
public function index(HtmxRequest $request): HtmxResponse { // whether the request is made via htmx $request->isHtmxRequest(); // whether the request is via an element using hx-boost attribute $request->isBoosted(); // the current browser url $request->getCurrentUrl(); // whether request is for history restoration after a miss in the local history cache $request->isHistoryRestoreRequest(); // the user response to an hx-prompt $request->getPromptResponse(); // the id of the target element if it exists $request->getTargetId(); // the id of the triggered element if it exists $request->getTriggerId(); // the name of the triggered element if it exists $request->getTriggerName(); // ... }
Response headers
public function index(HtmxRequest $request): HtmxResponse { // ... return $this->htmxRender('index.html.twig') // or new HtmxResponse() // Optional headers ->setLocation( "/location", [ 'source' => '', 'event' => '', 'handler' => '', 'target' => '', 'swap' => '', 'select' => '' 'values' => [], 'headers' => [], ] )) // set HX-Location header ->setPushUrl('/push') // set HX-Push-Url header ->setReplaceUrl('/replace') // set HX-Replace-Url header ->setReswap('outerHTML') // set HX-Reswap header ->setRetarget('#target') // set HX-Retarget header ->setReselect('#select') // set HX-Reselect header ->setTriggers('event') // set HX-Trigger header (simple) ->setAfterSwapTriggers('event1,event2') // set HX-Trigger-After-Swap header (multiple) ->setAfterSettleTriggers(['event' => ['key' => 'value']]); // set HX-Trigger-After-Settle header (with detail) }
Render template block
<!-- index.html.twig --> {% extends 'base.html.twig' %} {% block block1 %} <div id="block1"> Sample content... </div> {% endblock %}
public function index(HtmxRequest $request): HtmxResponse { return $this->htmxRenderBlock('index.html.twig', 'block1'); }
Redirect
public function index(HtmxRequest $request): HtmxResponse { return $this->htmxRedirect('https://htmx.org/'); }
Refresh
public function index(HtmxRequest $request): HtmxResponse { return $this->htmxRefresh(); }
Stop polling
<!-- index.html.twig --> {% extends 'base.html.twig' %} {% block content %} <div hx-get="/poll" hx-vals="js:{pollingIndex}" hx-trigger="every 2s"></div> <script type="text/javascript"> var pollingIndex = 0; document.body.addEventListener("polling", function(e) { pollingIndex++; console.log("Polling Index:", pollingIndex); }); </script> {% endblock %}
public function poll(HtmxRequest $request): HtmxResponse { $index = $request->get('pollingIndex', 0); if ((int)$index >= 10) { return new HtmxStopPollingResponse(); } return (new HtmxResponse())->setTriggers('polling'); }