Injecter du Twig dans du JS avec Assetic (.js.twig power)

Injecter du Twig dans du JS avec Assetic (.js.twig power)

En premier lieu, cet article est un compte rendu de la minute hack que j’ai réalisé il y a quelques jours.

N’étant pas un fervent défenseur d’Assetic, il m’est obligatoire de l’utiliser sur les projets que nous développons actuellement dans mon entreprise. Et par conséquent, l’utilisation de Assetic doit me rendre un maximum service, comme tout outils.

Un problème ? Une solution !

Voici ma problématique. Nous avons tous eu un jour besoin de construire une route dans un fichier JS. À ce problème, nous avons tous trouver une pseudo-solution.

  1. Vous injectez dans votre page (HTML) une variable JS avec du contenu Twig. Cette variable sera utilisée dans un fichier JavaScript chargé via Assetic. Efficace mais difficile à lire et pas très user-friendly.

    1
    2
    3
    4
    5
    6
    7
    8
    // template twig
    <script>
    var myVar = "{{ path('my_route') }}";
    </script>
    // fichier javascript
    $("#sort").on("change", function() {
    window.location.href = myVar + $(this).val();
  2. Vous utilisez FOSJsRoutingBundle et vous utilisez un catalogue des routes dans un fichier JS généré par le bundle. Avantage, les routes peuvent être générées très facilement, avec des paramètres liées au contexte. Inconvénient, l’utilisation d’un bundle supplémentaire, et l’obligation de déclarer les routes utilisables avec l’option expose=true. Cela peut potentionnellement exposer certaines routes dans le fichier contenant toutes les routes exposées.
    Parfois, l’utilisation de FOSJsRoutingBundle est un peu comme utilisé un bazooka pour tuer une mouche : ça coute beaucoup alors qu’on n’a pas besoin de faire grand chose. L’alternative, tenter d’injecter du Twig dans ses JS.

Dans quel cas d’utilisation, et quelles sont les limites ?

Attention, ce hack ne doit pas être utilisé à tout va dans la mesure où son utilisation est très limité.

Assetic compile des fichiers JavaScript d’une manière complètement détaché du contexte en cours. J’entends contexte par requête en cours. Si dans le fichier JS vous pouvez tenter d’utiliser tout et n’importe quoi, au moment de la génération du fichier, seul certaines variables seront disponibles.

  • Les variables globales de Twig
  • Les fonctions et les filtres Twig peuvent être utilisées
  • Les routes sont disponibles et peuvent être générées
    MAIS !! le contexte n’est pas disponible, n’espérait donc pas utilisé les paramètres de la requête en cours, via les variables Twig en tout cas.

Vous pouvez donc générer des routes sans paramètre dynamique ou avec des paramètres statiques, ou encore utiliser les paramètres globaux comme ceux injecté dans le parameter.yml.

Tout sur comment le mettre en place

Vous devez créer un filtre Twig pour que Assetic sache quoi faire de vos fichiers. Vous pouvez le placer dans src/AppBundle/Filter.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
<?php
namespace AppBundle\Filter;
use Assetic\Asset\AssetInterface;
use Assetic\Filter\FilterInterface;
use Symfony\Bundle\TwigBundle\TwigEngine;
/**
* @author Baptiste Donaux <baptiste.donaux@gmail.com>
*/
class TwigFilter implements FilterInterface
{
private $templating;
public function __construct(TwigEngine $templating)
{
$this->templating = $templating;
}
public function filterLoad(AssetInterface $asset)
{}
public function filterDump(AssetInterface $asset)
{
$content = $asset->getContent();
$content = $this->templating->render($asset->getSourceRoot()."/".$asset->getSourcePath());
$asset->setContent($content);
}
}

Vous devez déclarer ce filtre. Il s’agit d’un service que l’on va tagger comme étant un filtre d’Assetic. Vous pouvez le déclarer dans app/config/services.yml.

1
2
3
4
5
6
7
services:
assetic.filter.twig:
class: AppBundle\Filter\TwigFilter
arguments:
- @templating
tags:
- { name: "assetic.filter", alias: "twig" }

Dans votre template où vous souhaitez utiliser un fichier du type .js.twig, vous allez simplement déclarer le filtre Assetic.

1
2
3
4
5
{% javascripts filter="twig"
'@AppBundle/Resources/public/js/file.js.twig'
%}
<script type="text/javascript" src="{{ asset_url }}"></script>
{% endjavascripts %}

Voici le fichier file.js.twig utilisé dans mon cas.

1
2
3
$("#sort").on("change", function() {
window.location.href = "{{ path('my_route', {}) }}" + $(this).val();
});

Une fois le fichier généré voilà le rendu.

1
2
3
$("#sort").on("change", function() {
window.location.href = "/my_route" + $(this).val();
});

Conclusion, why not ?

L’utilisation de cette astuce doit être réellement mesurée car si cela permet d’injecter quelques variables ou quelques routes simples, les problématiques peuvent rapidement être plus importante. Dans ces cas, cette solution peut ne pas suffir.

Cette solution a également pour objectif de montrer qu’Assetic peut proposer des solutions à des problématiques, des solutions que Grunt, Gulp ou Brunch ne peuvent pas toujours proposer.

Un avis, un feedback ? Posez un commentaire ;)