Le front-end était géré par le backend
Un peu de JS par ci, par là
<fieldset>
<label for="age">
Votre age
<input id="age" />
</label>
<label for="beers" hidden>
Combien de bières avez-vous bu aujourd'hui ?
<input id="beers" />
</label>
<button type="submit">Envoyer</button>
</fieldset>
// Afficher le champs "beers" si l'utilisateur a moins de 18 ans
const age = document.querySelector('#age');
const beers = document.querySelector('label[for="beers"]');
age.addEventListener('input', (e) => {
if (e.currentTarget.valueAsNumber && e.currentTarget.valueAsNumber >= 18) {
beers.removeAttribute('hidden');
} else {
beers.setAttribute('hidden', true);
}
});
$('#age').on('input', function () {
const age = parseInt($(this).val(), 10);
$('label[for="beers"]').attr('hidden', isNaN(age) || age < 18);
});
PHP ne s'occupe que de la partie API
#[Route('', name: 'post_index', methods: ['GET'])]
public function index(Request $request, PostRepository $postRepository): Response
{
$page = $request->query->getInt('page', 1);
$paginator = $postRepository->findPaginated($page);
$totalPosts = count($paginator);
$pagesCount = ceil($totalPosts / PostRepository::POSTS_PER_PAGE);
return $this->render('post/index.html.twig', [
'posts' => $paginator,
'currentPage' => $page,
'pagesCount' => $pagesCount,
'totalPosts' => $totalPosts, // Pass total count if needed in template
'limit' => PostRepository::POSTS_PER_PAGE // Pass limit if needed
]);
}
#[Route('', name: 'api_post_index', methods: ['GET'])]
public function index(PostRepository $postRepository, Request $request): JsonResponse
{
$page = $request->query->getInt('page', 1);
$limit = $request->query->getInt('limit', 10);
$paginator = $postRepository->findPaginated($page, $limit);
$totalItems = count($paginator);
$pagesCount = ceil($totalItems / $limit);
$responseData = [
'data' => $paginator,
'pagination' => [
'currentPage' => $page,
'totalPages' => $pagesCount,
'totalItems' => $totalItems,
'itemsPerPage' => $limit,
]
];
// Return the structured data as JSON
return $this->json($responseData, Response::HTTP_OK, [], ['groups' => 'post:list']);
}
Donner des super pouvoirs à l'HTML
<fieldset x-data="{age: ''}">
<label for="age">
Votre age
<input
x-model="age"
id="age"
name="age"
type="number"
placeholder="Age"
autocomplete="given-name"
/>
</label>
<label for="beers" x-show="age && age >= 18">
Combien de bières avez-vous bu aujourd'hui ?
<input
id="beers"
type="number"
name="beers"
placeholder="Nombre de bières"
/>
</label>
<button type="submit">Envoyer</button>
</fieldset>
Piloter la mutation du DOM depuis le serveur
<table hx-indicator=".htmx-indicator">
<thead><tr><th>Name</th><th>Email</th><th>ID</th></tr></thead>
<tbody>
<tr></tr>
<tr></tr>
<tr></tr>
<tr hx-get="/users/?page=2" hx-trigger="revealed" hx-swap="afterend"></tr>
</tbody>
</table>
<img class="htmx-indicator" width="60" src="/img/bars.svg">
Live components (Symfony) & Livewire (laravel)
#[AsLiveComponent]
class ProductSearch
{
use DefaultActionTrait;
#[LiveProp(writable: true)]
public string $query = '';
public function __construct(private ProductRepository $productRepository)
{
}
public function getProducts(): array
{
// example method that returns an array of Products
return $this->productRepository->search($this->query);
}
}
<div {{ attributes }}>
<input
type="search"
data-model="query"
>
<ul>
{% for product in this.products %}
<li>{{ product.name }}</li>
{% endfor %}
</ul>
</div>
Comme avant mais en plus moderne
<article>
<h1><?= $post->title ?></h1>
<p><?= $post->content ?></p>
<comment-list post="<?= $post->id ?>"><comment-list>
</article>
Il faut souvent combiner