everlutionsk/ajaxcom-bundle
Everlution AjaxcomBundle integrates Ajaxcom JavaScript with Symfony and Twig, enabling AJAX-driven block rendering, flash messages, and configurable persistent or refreshable blocks for smoother frontend updates.
This is a small, opinionated bridge between Symfony controllers and the @everlutionsk/ajaxcom-js browser client. Instead of returning JSON data and re-rendering on the client, you return JSON fragments (server-rendered Twig) plus flash messages; the JS client swaps them into the DOM. Treat it as a lighter-weight, pre-Turbo/htmx take on partial AJAX updates.
Four steps to a working request:
composer require everlutionsk/ajaxcom-bundlenew Everlution\AjaxcomBundle\EverlutionAjaxcomBundle() in registerBundles() on legacy Symfony, or the usual config/bundles.php entry on Symfony 4+. There is no Flex recipe, so on modern Symfony you write the entry yourself.npm i @everlutionsk/ajaxcom-js, add an Encore entry (or copy the dist file) and include it in your base layout: {{ asset('build/ajaxcom.js') }}.ajaxcom-js README) — the bundle only produces the payload; the client decides where flash messages land.Where to look first, in this order:
vendor/everlutionsk/ajaxcom/src/ — the PHP core (Component, Processor). The bundle is a thin Symfony wrapper; almost all real API surface lives here.vendor/everlutionsk/ajaxcom-bundle/DependencyInjection/Configuration.php — the entire config tree. If it isn't there, it isn't configurable.Resources/views/ — the default flash message templates you can override.First use case: a form submit that replaces a list without a page reload.
use Everlution\Ajaxcom\Component;
public function create(Request $request, Ajaxcom $ajaxcom): Response
{
$form = $this->createForm(TaskType::class);
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
$this->getDoctrine()->getManager()->persist($form->getData());
$this->getDoctrine()->getManager()->flush();
$this->addFlash('success', 'Task created.');
}
$ajaxcom->createComponent()
->setSelector('#task-list')
->setHtml($this->renderView('task/_list.html.twig', [
'tasks' => $this->getDoctrine()->getRepository(Task::class)->findAll(),
]))
->setMethod(Component::METHOD_REPLACE);
return $ajaxcom->createResponse();
}
The single most important architectural rule: the Twig partial you render for the component is the same partial the full page renders. Do not build HTML strings in PHP. If the partial is reusable, the endpoint stays a three-line change.
One controller action, two response shapes. The dominant pattern is a form action that returns a redirect on a normal POST and a component response on an AJAX POST. Guard on the request, not on the route:
if (!$request->isXmlHttpRequest()) {
return $this->redirectToRoute('task_index');
}
Keeping both branches in one action means validation errors are handled once. On invalid, push the form itself back as a component so the user sees field errors:
$ajaxcom->createComponent()
->setSelector('#task-form')
->setHtml($this->renderView('task/_form.html.twig', ['form' => $form->createView()]))
->setMethod(Component::METHOD_REPLACE);
Multi-component responses. A single response can carry several components; the client applies them in order. This is the feature that saves the most client-side code — update the list, the counter, and clear the form in one round trip:
$ajaxcom->createComponent()->setSelector('#task-list')->setHtml($listHtml)->setMethod(Component::METHOD_REPLACE);
$ajaxcom->createComponent()->setSelector('#task-count')->setHtml((string) $count)->setMethod(Component::METHOD_REPLACE);
$ajaxcom->createComponent()->setSelector('#task-form')->setHtml($emptyFormHtml)->setMethod(Component::METHOD_REPLACE);
Method choice matters. REPLACE (inner HTML) is the default mental model; use APPEND/PREPEND for "load more" and infinite scroll so you don't re-render the whole list; REMOVE for deletes — it avoids shipping an empty fragment over the wire. Because components are applied sequentially, ordering is your only dependency mechanism: append before you update the counter.
Flash messages are free. After $this->addFlash(...), the bundle renders the flashed messages through its Twig template and ships them alongside components; the JS client pops them into your toast container. You never write flash HTML by hand. Override the template by copying the bundle's view into templates/bundles/EverlutionAjaxcomBundle/ and pointing config at it.
Wrap the boilerplate early. By the third endpoint you'll be repeating createComponent()->setSelector()->setHtml($this->renderView(...))->setMethod(...). Put a component(string $selector, string $template, array $context, string $method = Component::METHOD_REPLACE) helper on a base controller or a small AjaxcomRenderer service. This also gives you one place to add data-* attributes or instrumentation later.
Payload data for client behaviour. Components can carry a data payload for the JS handler (scroll-to, highlighting, re-initialising a widget after a swap). Use it instead of putting behaviour flags in the HTML — e.g. after replacing a list, tell the client to re-run your JS initialiser. Anything the swap destroys (select2, datepickers, tooltips inside the replaced subtree) must be re-initialised, and the payload is the clean hook for that.
Compose with the flash bag, not against it. The bundle's value is highest on pages where you'd otherwise duplicate flash rendering for HTML and JSON. If a page lives entirely behind AJAX, drop flash rendering from the layout and let the client own it.
This is a legacy-shaped bundle. The README still shows AppKernel, there are 6 stars and zero dependents, and no Flex recipe. Before adopting, run composer why-not symfony/framework-bundle against the latest release (2024-11-07) and read composer.json constraints. Budget an hour for wiring services.yaml/bundles.php manually if you're on Symfony 5+.
Verify the method names against the source, not the README. The README is thin and the PHP core is the real contract (Component::METHOD_*, the facade's factory methods). Open vendor/everlutionsk/ajaxcom/src/Component.php once at the start; it takes two minutes and prevents an afternoon of Call to undefined method.
Flash bag consumption is the classic double-render bug. If your base layout renders app.flashes for full pages and the AJAX handler also reads them, you can get duplicated toasts or messages that vanish before the AJAX response builds. Decide on one owner per request type, and prefer peek semantics if you must read twice.
Don't return the component — return the response. The processor produces the JSON envelope; returning a component object directly gets you a serialisation error or an empty 200. Always return $ajaxcom->createResponse();.
Content-Type and jQuery/axios defaults. The client expects a JSON envelope. If your front-end library mangles Accept headers or a proxy caches the POST, you get a parse error that looks like a server bug. Confirm application/json in the Network tab before debugging Twig.
Selectors are your API and they can silently no-op. If a selector matches nothing, the client typically does nothing — no error, no console message. When a swap "doesn't work," check that the element exists in the DOM at the time the response is applied, especially after a previous component replaced its parent.
Whitespace and asset() paths bite first. {{ asset('build/ajaxcom.js') }} requires the build to have run and the manifest to be present; a stale public/build produces a 404 that only shows on AJAX-triggering pages. Also trim component HTML if you diff responses in tests — Twig indentation will make assertions brittle.
Form validation still needs CSRF. AJAX POSTs go through the normal firewall/CSRF layer. If you're posting a form's fields manually, include _token; if you're serialising the real form, Symfony handles it — but confirm the token isn't being dropped by your JS serialisation.
Testing. The response is a plain JSON envelope, so assertions are cheap and stable if you assert on json_decode($response->getContent(), true) structure rather than raw strings. Test both branches of the action (HTML redirect and JSON components) — the non-AJAX branch is the one that silently breaks after refactors.
Extension points, in increasing invasiveness: override the flash templates via the bundle view path → supply your own Twig partials per component (the intended extension) → define a custom component class in the core library for a repeated payload shape → decorate the Ajaxcom facade service in services.yaml if you need cross-cutting behaviour (default payload data, logging, dev-only response dumping).
Know when not to use it. It's a fragment-swapping tool, not a data API, and it has no state reconciliation. For file uploads with progress, streaming, or anything where the client needs the data (not the HTML), return plain JSON. Mixing both styles on one page is fine, but keep the boundary explicit so nobody wonders why one endpoint returns {components: [...]} and another returns {tasks: [...]}.
Pin the version. With no dependent ecosystem and a small maintainer surface, "everlutionsk/ajaxcom-bundle": "^0.9"-style loose constraints plus a composer.lock commit is the pragmatic posture; a surprise BC break in a 0.x release will not have a Stack Overflow answer waiting for you.
How can I help you explore Laravel packages today?