Bonjour,

Voila je rencontre un petit problème avec mon code.

Ce que je fais

J'aimerais me perfectionner dans symfony et réussi à intégrer des composants VueJS, Preact ou autre dans mon application symfony. J'ai regardé pas mal de tutoriels concernant l'installation mais je trouve quasiment que des exemples pour faire une single app application.

Voilà ce que j'ai fais pour le moment

  1. Installer toutes les dépendances : encore, vuejs, ...
  2. Activer dans la config de encore le vue loader.
  3. Je créé un composant TestComposant dans le dossier components, fichier TestComposant.vue
    <template>
    <h1>Bibou c'est un test</h1>
    </template>
    <script>
    export default {
      name: 'testcomposant'
    }
    </script>
  4. Dans mon fichier assets/app.js
    
    // [...]
    import TestComposant from './components/TestComposant';
    import Vue from '...'; // J'ai pas le code sous les yeux je fais ça de mémoire

new Vue({
el: '#app',
components:{
TestComposant
}
});

5. Ajouter le liens du app.js dans le base template de twig avec la fonction `entry...tags('app')`

### Ce que je veux

Je voudrais pouvoir ou je veux dans un template twig mettre un ou plusieurs composants par exemple `<testcomposant></testcomposant>` . Je comprends pas trop le #app, est-ce la bonne pratique d'entourer tous les containers une div avec l'id app?

```html
<!--base.html.twig-->
<!--[...]-->
<!-- Encore une fois c'est de tête donc ce n'est peut être pas exactement la bonne syntaxe mais vous avez compris l'idée :) -->
<body>
<div id="app">
  {% block 'body' %}{endblock}
  </div
</body>

Ce que j'obtiens

J'ai pas d'erreur en particulier, j'arrive bien a afficher mes coposants mais uniquement dans une div avec id="app". Je me demande déjà si c'est la bonne façon de déclarer ces composant dans ce cas (cf. app.js).
Quelle sont les bonnes pratiques?

Merci d'avance et bonne année à tous !

2 réponses


new Vue({
  el: '#app', // le noeud racine de ton application
  components:{
      TestComposant // un composant de ton application
  }
})

Vue intègre la notion de portée (le Scope). Un élément (variable, fonction, composant, etc.) créé dans ton application sera accessible uniquement dans le noeud correspondant ; jamais à l'extérieur.

Tous tes composants ne doivent pas avoir l'id app, uniquement ton application principale.
Ainsi tu pourrais avoir dans une même page, deux applications distinctes selon la structure suivante:

  • app-1
    • composant-1
    • composant-2
  • app-2
    • composant-3
    • composant-4

TestComposant est un composant, il peut être appelé plusieurs fois, tant qu'il est enfant du noeud #app:

<TestComposant></TestComposant>
<TestComposant></TestComposant>
<TestComposant></TestComposant>

Tu peux modifier le comportement de tes composants en leur injectant des données via des paramètres

<TestComposant say="hello"></TestComposant>
<TestComposant say="bonjour"></TestComposant>
<TestComposant say="Aloha"></TestComposant>

Enfin si tu désire faire une application Symfony avec Vue en front, sans que cela ne soit une single app, tu as deux variantes:
La première consiste à rendre vue accessible à toutes les pages en l'ajoutant dans le fichier base.html.twig

<!--base.html.twig-->
{% block javascripts %}
<script type="application/javascript">
var app = new Vue({
    el: '#app',
    /* ... */
})
</script>
{% endblock javascripts %}
<body>
    <div id="app">{% block body %}{endblock}</div>
</body>

Le problème se fera ressentir lorsque tu voudras configurer Vue depuis les fichiers twig (ceux appelés par tes controlleurs) :
Ajouter des composants, définir les données, définir les méthodes, ajouter une librairie externe qui doit être exécuté lorsque l'app est montée (typiquement Moment.js qui doit être instancié lorsque Vue est montée pour fonctionner correctement, non pas depuis l'extérieur de l'application).

La seconde solution (celle que j'ai adoptée pour l'instant) est d'instancier Vue dans chacun des fichiers twig où elle est nécessaire et de lui injecter les données fournies par Symphony:

<!-- fichier twig quelconque -->
{% block javascripts %}
{{ parent() }} <!-- ici on inclus les scripts déjà présents dans le fichier base.html.twig -->
<script type="application/javascript">
var app = new Vue({
    el: '#app',
    data: {
        <!-- ici on transmet les données de Symphony vers notre app Vue -->
        produits: {{ products|raw }}
    }
})
</script>
{% endblock javascripts %}

Attention toutefois !
Transmettre des données de Symfony vers Vue, c'est convertir des données d'une base relationnel en entitée (rôle de Doctrine), puis des entitées en Json.
Symfony NE CONVERTI PAS des entitées en Json, ce travail tu devras l'effectuer par toi même dans chacun des contrôleurs transmettant des données vers Vue.
Pour ce faire, je peux te conseiller la librairie JMS\Serializer qui fait cela vraiment bien et nous facilite grandement le travail.

De plus, lorsque tu effectueras la conversion entitée --> json avec cette librairie (ou même une autre), tu effectueras implicitement du Eager loading, selon ta configuration.

Exemple: Deux entitées liées, Client et Facture où la liaison est Facture.client = Client.id et que tu converties une Facture en Json. Doctrine effectura un appel à ta base de données pour que l'attribut Facture.client soit complet. Pire encore ce problème s'applique en cascade ! Doctrine effectuera encore des appels à ta base de données pour s'assurer que l'entitée Client soit complet si elle même inclue d'autres entitées.

Deux solutions (de mémoire) pour résoudres ces problèmes:

  1. Changer la stratégie de chargement des tables liées et faire du Lazy Loading. (Plus tout à fait sûr que ça fonctionne)
  2. Utiliser les annotations de la librairie JMS\Serializer en excluant les attributs inutiles.

Espérant que ça répond à certaines de tes interrogations

Salut Twentyfour,
Merci beaucoup pour cette réponse très complète. Je pense que je vais donc partir sur ta façon : instancier Vue dès que j'en ai besoin ça me parait plus propre. Et merci pour l'info sur la sérialisation ça me servira sur mes prochains projets :D
A+