TP n°6 - Interfaces habillées en CSS avec Bootstrap

Table des matières

1. Introduction

Expérimenter la mise en place d’un habillage graphique des pages de l’application « fil-rouge » ToDo en utilisant les feuilles de style CSS.
Pour ce faire, on utilisera le framework Bootstrap, qui permettra notamment de doter l’application d’une présentation réactive compatible avec la consultation sur mobile.

Différents outils de mise au point seront mis à contribution pour ce faire, comme les outils du développeur de FireFox, et la barre d’outils Symfony.

2. Étape 1 : Intégration de Bootstrap dans l’application ToDo

Comprendre la façon dont le code des gabarits va être modifié pour intégrer Bootstrap, pour doter l’application d’une couche de présentation en CSS.

Nous allons modifier l’application fil-rouge todo-app déjà étudiée dans la séance précédente, pour la doter d’une couche de présentation avec des feuilles de style CSS, en intégrant le framework Bootstrap. L’application est fonctionnellement équivalente au TP précédent, toujours uniquement en mode consultation. Ce qui importe dans cette nouvelle version est la façon de mettre en forme avec du CSS nos pages HTML.

2.1. Choix d’un thème Bootstrap

Pour mettre en œuvre Bootstrap, nous allons intégrer une distribution provenant du projet « Start Bootstrap ».

Start Bootstrap propose différents modèles (templates) prêts à l’emploi, sous forme de distributions auto-suffisante de Bootstrap, à télécharger, incluant des exemples.

Nous vous suggérons de partir du template d’application « Bare » :

An unstlyed [ unstyled ?] starter template for Bootstrap 5 with predefined file paths - a great boilerplate for starting a new Bootstrap project

bare.jpg

Figure 1 : template « Bare » de StartBootstrap (source : https://startbootstrap.com/templates)

Vous pourrez adapter la mise en forme ultérieurement selon vos goûts, en explorant d’autres variantes de mise en forme, de templates StartBootstrap, de thèmes Bootstrap

Consultez les liens ci-dessus pour accéder à la documentation et aux exemples.

2.2. TODO Étape 1-a : Téléchargement de « Start Bootstrap - Bare »

Récupération dans le projet d’une distribution de BootStrap prête à l’emploi.

  1. Positionnez-vous dans le répertoire du projet Symfony de l’application ToDo.

    cd $HOME/CSC4101/tp-05/todo-app/
    
  2. Récupérez la distribution Start Bootstrap Bare depuis le lien suivant, et extrayez le contenu dans le répertoire public/. Par exemple sous Linux :

    cd public
    wget https://github.com/startbootstrap/startbootstrap-bare/archive/gh-pages.zip
    unzip gh-pages.zip
    
       Archive:  gh-pages.zip
    e1b6c59cda92bed121c6785169c401b5810ee168
       creating: startbootstrap-bare-gh-pages/
       creating: startbootstrap-bare-gh-pages/assets/
      inflating: startbootstrap-bare-gh-pages/assets/favicon.ico  
       creating: startbootstrap-bare-gh-pages/css/
      inflating: startbootstrap-bare-gh-pages/css/styles.css  
      inflating: startbootstrap-bare-gh-pages/index.html  
       creating: startbootstrap-bare-gh-pages/js/
      inflating: startbootstrap-bare-gh-pages/js/scripts.js  
    
    

    ces ressources statiques ont été extraites dans le répertoire public/startbootstrap-bare-gh-pages/ du projet.

2.3. TODO Étape 1-b : Consultation de la page d’exemple

Comprehension du principe d’adaptation responsive du contenu des pages Web

Vous avez installé les fichiers d’exemple de Bootstrap bare, dans la partie publique du projet Symfony (le répertoire public/), c’est à dire que les fichiers qui s’y trouvent sont accessibles par HTTP comme autant de ressources statiques qui sont servies par le serveur HTTP directement, sans passer par l’exécution de l’application en PHP.

C’est tout à fait adapté à la transmission vers le navigateur Web (client HTTP) des images, feuilles de styles CSS et autres fichiers (contenant du code JavaScript), par exemple.

On va tester le résultat dans le navigateur.

  1. Lancez le serveur Web Symfony depuis la racine du projet, comme d’habitude

    cd ..
    symfony server:start
    
  2. Consultez la page de démo de Bootstrap bare, que vous avez extraite dans public/startbootstrap-bare-gh-pages/, en la chargeant dans votre navigateur, par exemple sur l’URL http://localhost:8000/startbootstrap-bare-gh-pages/index.html

    La page « A Bootstrap 5 Starter Template » s’affiche avec son menu sur fond noir affiché en haut de la page.

  3. Utilisez l’outil « Vue adaptative » des outils du développeur de Firefox pour afficher une prévisualisation de ce que donnerait la consultation de cette page avec un ordiphone dont l’écran est plus petit.

    firefox-menu-vue-adaptative.png

    Figure 2 : Accès au menu « Vue adaptative » de Firefox

    Remarquez comme les menus sont masqués derrière un bouton « big mac » (à trois traits horizontaux) :

    firefox-vue-adaptative.png

    Figure 3 : Simulation d’écran d’ordiphone en mode portrait

    Le contenu de la page est adaptatif, responsive : des éléments sont masqués dynamiquement, selon les besoins, pour s’adapter à la taille de l’écran de l’utilisateur.

    Ça fonctionne, mais pas grâce à Symfony : tout ceci est du contenu statique, du point de vue de notre code PHP, comme en témoignent les logs qui restent silencieux.

    Sous le capot, il y a plein de CSS et de Javascript à l’oeuvre. Voyons ça.

  4. Utilisez le « Moniteur Réseau » des outils du développeur Firefox pour afficher le chargement des ressources

    bootstrap-resources-network-view.png

    Figure 4 : Examen du chargement des ressources additionnelles

    On voit que la page inclut le chargement d’une feuille de style CSS et 2 scripts JavaScript. Dans le moniteur réseau, on peut cliquer sur chaque ressource pour voir le chemin détaillé, respectivement dans les sous-répertoires css/ et js/ de startbootstrap-bare-gh-pages/.

    On remarque qu’un des fichiers JavaScript est récupéré depuis le Web (depuis cdn.jsdelivr.net), alors que l’autre est local (localhost).

    Il se peut que vous ne voyez pas exactement tous les fichiers apparaître comme dans la copie d’écran ci-dessus.

    C’est probablement dû au cache du navigateur, qui permet d’économiser les transferts réseau, quand des ressources déjà connues sont inclues dans des pages. Plutôt que de redemander le transfert de la ressource avec une requête GET au serveur, le navigateur charge la copie qu’il a gardé dans son cache. Dans le moniteur réseau on voit la mention du cache dans la colonne « Transfert ».

    Pour forcer le rechargement de toutes les ressources, appuyez sur la touche « SHIFT » (« Maj »), en même temps que vous rechargez la page avec le bouton de rechargement (ou « SHIFT » + « Ctrl-R »). Vous verrez alors les requêtes GET prendre plus de temps, et la taille du chargement dans la colonne « Transfert ».

    Essayez, et vous devriez maintenant voir tous les scripts CSS chargés bien mentionnés dans le moniteur réseau.

Examinons maintenant le code de la page pour comprendre en détail comment ces ressources sont incluses.

2.4. TODO Étape 1-c : Examen de la structure du code HTML

Examen du source HTML des pages d’exemple de Bootstrap

On va voir le code HTML de cette page de démo, pour voir précisément comment les ressources sont incluses.

  1. Chargez le code source de public/startbootstrap-bare-gh-pages/index.html dans Eclipse (ou dans le navigateur : « voir le code source de la page » / Ctrl-U),
  2. Identifiez où sont chargées les ressources de Bootstrap :
    • la feuille de style CSS (Bootstrap core CSS), référencée depuis l’entête HTML :

      <head>
        ...
        <!-- Core theme CSS (includes Bootstrap)-->
        <link href="css/styles.css" rel="stylesheet" />
      </head>
      

      Quand le navigateur Web charge la page public/startbootstrap-bare-gh-pages/index.html qui contient <link href="css/styles.css" rel="stylesheet">, deux requêtes HTTP se produisent :

      • GET startbootstrap-bare-gh-pages/index.html
      • GET startbootstrap-bare-gh-pages/css/styles.css

      L’URL de la ressource d’accès à la feuille de style est ici relative à la page (href="css/styles.css" indique le chargement de la ressource styles.css depuis le sous-répertoire "css/").

      On verra ultérieurement comment le CSS s’applique au contenu… mais on doit d’abord terminer de cabler le chargement de ce fichier qui définit les règles CSS à appliquer à nos pages.

  • les scripts Javascript (Bootstrap core JavaScript) chargés à la fin du corps du HTML :

               <!-- Bootstrap core JS-->
                    <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.2.3/dist/js/bootstrap.bundle.min.js"></script>
               <!-- Core theme JS-->
               <script src="js/scripts.js"></script>
       </body>
    
    </html>
    

    Nous étudierons le rôle du langage JavaScript dans une prochaine séance. Pour l’instant, il est juste utile de savoir que cela permet de déclencher des programmes qui s’exécutent dans le navigateur pour, par exemple, modifier l’affichage de la page. Comme on le voit ici, Bootstrap nécessite le chargement de scripts écrits en JavaScript, par exemple pour gérer le repositionnement des éléments de la page quand la largeur de l’écran change.

    L’un des scripts (scripts.js) nous a été fourni dans l’archive de StartBootstrap Bare, puisqu’il est local (dans le sous-répertoire js/). L’autre (bootstrap.bundle.min.js) fait référence à une version disponible en ligne sur un CDN (Content Delivery Network), qui héberge une copie de la version correspondante de la partie Javascript nécessaire à Bootstrap.

    Si vous êtes curieux, vous constaterez d’ailleurs qu’il n’y a pas grand chose d’intéressant dans js/script.js

On va donc pouvoir s’inspirer du code HTML de cette page d’exemple, pour ajouter ce qu’il faut dans les gabarits de notre projet.

Ainsi, les mêmes ressources seront utilisables, pour autant qu’on utilise le même chemin d’accès dans l’URL (startbootstrap-bare-gh-pages/css/..., etc.).

2.5. Présentation d’un mécanisme simple d’intégration des ressources pour Symfony (assets)

Nous allons présenter dans cette section un mécanisme qui peut être utilisé par Symfony pour permettre l’inclusion de ressources statiques dans un site, avec la fonction asset() de Twig.

Nous détaillons le principe de ce mécanisme dans plusieurs sous-sections.

Lisez-les, et vous pourrez passer à la section suivante, qui vous guidera dans les différentes opérations à effectuer.

Comme on l’a vu en regardant le code HTML de la page d’exemple de Bootstrap bare, pour ajouter BootStrap dans une page HTML, il faut que celle-ci charge les ressources (statiques) correspondant aux feuilles de style CSS, et aux scripts JavaScript associés.

Si la page est dynamique, c’est à dire que son code HTML est généré par l’exécution de l’application Symfony, il faut agir sur la fabrication de la page, donc au niveau des gabarits Twig.

Dans ce qui suivra on s’inspirera du contenu du fichier HTML d’exemple qui est fourni dans la distribution « Bare » de StartBoostrap (présent dans public/startbootstrap-bare-gh-pages/index.html), pour y reprendre des bouts de code HTML, et les reporter dans les gabarits Twig de notre application.

Twig intègre une fonction, asset( ) qui permet de gérer l’inclusion de telles CSS et JavaScript, mais aussi d’images.

Examinons donc tout d’abord le principe du fonctionnement de asset().

Lisez d’abord les explications de la section ci-dessous. Vous ne passerez aux modifications correspondantes, qu’après avoir tout lu.

2.5.1. Chemin d’accès aux ressources statiques

On souhaite modifier le gabarit Twig de base (base.html.twig) pour y intégrer les en-têtes HTML nécessaires, qui font référence aux ressources des fichiers CSS.

Pas de mystère : on l’a vu ci-dessus, c’est réalisé via la balise standard de HTML : <link rel="stylesheet" href=".... On n’a qu’à l’ajouter dans le code HTML dans base.html.twig.

On va devoir indiquer, dans l’attribut href de la balise <link>, le chemin d’accès Web à la ressource. Ce chemin (path) devra être exprimé du point de vue du serveur Web de Symfony, pour qu’il trouve bien ces ressources statiques là où elles sont présentes dans le projet.

Comme on a installé la distribution Start Bootstrap à l’intérieur du répertoire public/ du projet (dans un sous-répertoire dédié : startbootstrap-bare-gh-pages/), la ressource peut être servie par notre serveur Web (comme on l’a vu ci-dessus).

On peut donc spécifier le chargement des feuilles de style via l’ajout de ce code HTML dans notre gabarit de base :

<!-- Core theme CSS (includes Bootstrap)-->
<link href="/startbootstrap-bare-gh-pages/css/styles.css" rel="stylesheet">

Dans notre environnement de développement Symfony, si on navigue sur l’interface de l’application, cela aura donc pour conséquence que le navigateur charge aussi la ressource présente à l’URL http://localhost:8000/startbootstrap-base-gh-pages/css/styles.css.

Cela se traduira bien par la requête auprès du serveur HTTP, du chemin /startbootstrap-base-gh-pages/css/styles.css. C’est correct, le répertoire public/ du projet Symfony contient bien les fichiers correspondants, dans un sous-répertoire startbootstrap-base-gh-pages/, là où nous avons recopié la distribution Boostrap provenant de « Start Bootstrap ».

Le fichier de feuille de style devrait donc bien être chargé depuis le répertoire où il a été installé (public/startbootstrap-base-gh-pages/css/styles.css).

Ça doit fonctionner. Mais il y a un petit inconvénient.

2.5.2. Inconvénient de la configuration des chemins « en dur »

En effet, dans la vie d’un site Web, il arrive souvent qu’on doive changer de jeu de feuilles de style.

Imaginons qu’est arrivée la saison des soldes. On pourrait vouloir pointer non-plus sur le style StartBootstrap bare mais sur StartBootstrap sales.

On pourrait ainsi souhaiter avoir plusieurs jeux de ressources statiques dans différents sous-répertoires de public/ et vouloir les reconfigurer simplement sans retoucher tout le code HTML des gabarits.

Si l’on souhaite changer de jeu de feuille de style, le fait de mentionner startbootstrap-bare-gh-pages « en dur » dans le code du gabarit est assez peu élégant.

Le mécanisme asset() permet d’y remédier simplement.

2.5.3. Principe de fonctionnement de asset()

Pour spécifier les chemins, on va donc préférer ajouter un degré de flexibilité et utiliser la macro asset() de Twig. Elle va faire le lien avec l’emplacement dans lequel vous avez extrait la distribution « Bare » de StartBoostrap (startbootstrap-bare-gh-pages/), pour dire où sont les « assets », les ressources du site.

On pourra ainsi configurer l’emplacement des ressources (assets) de Symfony pour pointer dans ce sous-répertoire. Cela reposera sur un fichier de configuration du projet Symfony, config/packages/framework.yaml (écrit en syntaxe YAML), qui spécifiera la déclaration suivante :

framework:
  ...
    assets:
        base_path: '/startbootstrap-bare-gh-pages'

Ceci permettra d’utiliser la fonction asset() de Twig dans les gabarits, de façon à aller charger des ressources statiques présentes à l’intérieur du sous-répertoire spécifié dans base_path, startbootstrap-bare-gh-pages.

On pourra alors utiliser asset() pour spécifier les chemins dans nos gabarits Twig, toujours avec la balise <link>.

On aura juste à intercaler un appel à asset() :

<!-- Core theme CSS (includes Bootstrap)-->
<link href="{{ asset('css/styles.css') }}" rel="stylesheet">

Ce mécanisme nous permettra d’effectuer un changement de jeu de feuilles de style par une simple reconfiguration de la déclaration de framework / assets / base_path dans le fichier de configuration du projet, sans rien à retoucher dans les sources des gabarits Twig.

Si vous décidez d’ajouter des images dans votre projet, vous aurez à définir de la même façon leur emplacement pour qu’elles s’affichent correctement dans les pages.

Si tout ceci est clair, passons à la pratique pour modifier nos gabarits dans Todo afin d’utiliser asset() pour inclure Bootstrap dans nos gabarits.

2.6. Étape 1-d : Intégration de Bootstrap dans vos templates

Passons maintenant à la modification effective de l’application ToDo pour tester l’ajout des feuilles de style de Bootstrap.

Bootstrap est prêt à être intégré dans les gabarits pour que le HTML l’utilise, pourvu qu’on inclue le bon jeu de feuilles de style. C’est ce que nous allons faire maintenant.

2.6.1. TODO Intégration des feuilles de style CSS

  1. Modifiez le contenu du fichier config/packages/framework.yaml pour ajouter la section assets dans framework (attention à respecter l’indentation faite par des espaces successifs) :

    framework:
      ...
        assets:
            base_path: '/startbootstrap-bare-gh-pages'
    

    Attention, dans certains cas, Eclipse est susceptible de remplacer les caractères espace par des tabulations, dans le fichier yaml… ce qui posera des problèmes à l’exécution.

    Attention à bien vérifier après le copier-coller au cas où des tabulations apparaîtraient.

  2. Modifiez templates/base.html.twig pour remplacer tout le bloc Twig stylesheets par le code ci-dessous :

    <!DOCTYPE html>
    <html>
        <head>
          ...
          {% block stylesheets %}
            <!-- Core theme CSS (includes Bootstrap)-->
            <link href="{{ asset('css/styles.css') }}" rel="stylesheet">
          {% endblock %} {# stylesheets #}
          ...
    

    Vous pouvez écraser l’ancien code qui était présent dans le bloc stylesheet.

    Ce code, fourni initialement dans le squelette d’application, avait été inclus par défaut, lors de l’intégration d’EasyAdmin dans le squelette du projet Todo.

    Il fonctionnait grâce à un autre mécanisme utilisable dans Symfony : « Webpack Encore ». Mais il s’avère plus lourd à mettre en oeuvre que les assets qui sont suffisants pour une application basique comme la notre.

    De cette façon, pour la plupart des gabarits, ce block stylesheets par défaut contiendra la bonne valeur, incluant la feuille CSS de Bootstrap. Mais si une page particulière doit redéfinir d’autres feuilles de style, elle pourra toujours surcharger ce bloc pour créer une spécialisation locale.

2.6.2. TODO Ajout du chargement des scripts JavaScript

De façon similaire à ce que vous avez fait pour le bloc stylesheets, intégrez le chargement des scripts JavaScript nécessaires à Bootstrap.

Supprimez la déclaration du bloc Twig javascript de l’en-tête <head>, qui utilisait « Webpack Encore ». Puis recopiez les balises <script du fichier d’exemple de Bootstrap bare, pour les intégrer via asset() au bon endroit dans le gabarit de base, dans un bloc javascripts présent en fin de balise <body>.

Au final vous obtenez le contenu suivant pour templates/base.html.twig :

<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8">
        <title>{% block title %}Welcome!{% endblock %}</title>
        <link rel="icon" href="data:image/svg+xml,<svg xmlns=%22http://www.w3.org/2000/svg%22 viewBox=%220 0 128 128%22><text y=%221.2em%22 font-size=%2296%22>⚫️</text></svg>">
        {% block stylesheets %}
            <!-- Core theme CSS (includes Bootstrap)-->
            <link href="{{ asset('css/styles.css') }}" rel="stylesheet" />
        {% endblock %} {# stylesheets #}
    </head>
    <body>
        {% block body %}{% endblock %}

        {% block javascripts %}
            <!-- Bootstrap core JS-->
            <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.2.3/dist/js/bootstrap.bundle.min.js"></script>
            <!-- Core theme JS-->
            <script src="{{ asset('js/scripts.js') }}"></script>
        {% endblock %} {# javascripts #}
    </body>
</html>

Testez :

  1. Lancez l’exécution de l’application Symfony (server:start) dans un terminal.
    • Si jamais vous avez une erreur sur la fonction asset() manquante, il est nécessaire d’installer le composant ad-hoc via une commande du type :

      symfony composer require symfony/asset
      
  2. Testez le chargement des pages, par exemple sur http://localhost:8000/todo/
  3. Observez les requêtes HTTP transmises par le navigateur dans l’outil Moniteur Réseau du développeur Web.

    Vous devez constater qu’une ressource styles.css est bien chargée par le navigateur (200)

  4. Observez les traces d’exécution du serveur Web dans le terminal

    Vous devez observer également un code de réponse 200, pour la ressource située dans /startbootstrap-bare-gh-pages/css/styles.css

    Ce « jeu de piste » paraît un peu compliqué, mais il nous montre que la macro asset() a bien fait le job.

  5. Observez également comment les fichiers JS sont bien chargés via des requêtes HTTP supplémentaires.

2.6.3. TODO Observations du comportement de Bootstrap

Si vous rechargez les pages de l’application, elles incluent désormais la mise en forme par défaut de BootStrap.

Vous devriez voir immédiatement un impact sur la mise en forme des tableaux des pages de consultation des listes de tâches ou des attributs d’une tâche.

Si vous testez avec l’outil de vue adaptative, vous constaterez que certains contenu s’adaptent bien à la taille de l’écran.

Certains éléments de Bootstrap ont un effet immédiat sur le contenu des pages Web… mais d’autres nécessitent de générer du HTML contenant les bonnes balises ou des propriétés ad-hoc comme les class utilisées par BootStrap.

On ne couvrira pas dans le cours comment tirer complètement parti de Bootstrap, mais sachez que certains composants de Symfony sauront (presque) automatiquement en tirer parti.

3. Étape 2 : Intégration des menus pour Bootstrap

Ajout dans l’application d’un composant de gestion de menus de navigation.

La plupart des applications Web intègrent des menus facilitant la navigation dans les grandes fonctions de l’application.

Nous allons ajouter un menu utilisant ces fonctionnalités de Bootstrap dans l’application ToDo.

Pour commencer, on va effectuer un ajout « manuel » du code gérant les menus.

3.1. TODO Étape 2.a : Test de menus codés « en dur »

Ajout du code HTML générant un menu avec Bootstrap, en recopiant le code HTML du fichier d’exemple.

La page d’exemple de Start BootStrap Bare contient un exemple de menu intégré dans une section <nav> du HTML.

Le principe d’organisation des menus à l’intérieur de cette section du HTML, est l’utilisation de listes à puces qui constituent une arborescence de liens, et que les feuilles de style de Bootstrap affichent sous forme de menus.

La documentation de Bootstrap explique comment on peut configurer les composants de navigation, mais plutôt que de lire toute la documentation, on va recopier l’exemple directement.

  1. Copiez le contenu de la section <nav> de public/startbootstrap-bare-gh-pages/index.html (à partir de <!-- Responsive navbar-->)
  2. Collez-le dans le gabarit de base templates/base.html.twig, au bon endroit, à l’intérieur du block HTML <body>.

    On va placer le contenu de la balise <nav> au début de la balise <body>, avant l’inclusion du block Twig body destiné à contenir ce que fournissent les pages. Ainsi, le menu apparaîtra dans toutes les pages, en premier.

    Vous obtenez normalement quelque-chose du style :

    ...
    
    <body>
     <!-- Responsive navbar-->
     <nav class="navbar navbar-expand-lg navbar-dark bg-dark">
         <div class="container">
           <a class="navbar-brand" href="#">Start Bootstrap</a>
    
           ...
         </div>
     </nav>
    
     {% block body %}{% endblock %}
    
     {% block javascripts %}
    
       ...
    
     {% endblock %} {# javascripts #}
    </body>
    
  3. Observez le résultat dans la page http://localhost:8000/todo/, avec l’apparition d’un menu.
  4. Utilisez l’inspecteur HTML des outils du développeur dans le navigateur pour repérer les balises concernées (<ul>, <li>, <a>, …).

    nav-bar-inspector.png

    Figure 5 : Inspection du contenu du HTML pour la barre de menu

  5. Les liens des entrées de menu sont-elles actives ? Pourquoi ?

    Indice : regardez la la valeur de l’attribut href des balises HTML <a (liens) présentes dans les balises <li> (entrées de menus). Ce ne sont pas des liens vers d’autres pages… juste des liens relatifs dans la page courante.

  6. Adaptez le code du gabarit pour faire fonctionner un menu « Todo », contenant des entrées de menus actives, qui renvoient vers les routes de votre contrôleur :

    • « Tasks » : todo_list
    • « Tags » : tag_list

    Vous utiliserez pour ce faire l’instruction path() de Twig, pour spécifier les routes à intégrer dans les chemins des attributs href.

    Vous devriez par exemple obtenir quelque-chose du genre :

      <nav class=...
        <div class="container">
            <a class="navbar-brand" href="{{ path('home') }}">Todo app</a>
            <button class="navbar-toggler" ...
            <div class="collapse navbar-collapse" ...
                <ul class=...
                    <li class="nav-item"><a class="nav-link" href="{{ path('todo_list') }}">Tasks</a></li>
                    <li class="nav-item"><a class="nav-link" href="{{ path('tag_list') }}">Tags</a></li>
    
                    ...
                </ul>
            </div>
        </div>
    </nav>
    

Testez que le menu est maintenant opérationnel.

Par contre, sa mise-à-jour au fur et à mesure de l’ajout de nouvelles routes dans l’application est un peu pénible : il faut se replonger dans la structure du HTML de cette section <nav>.

Voyons comment on peut configurer la structure de ces menus plus simplement.

3.2. TODO Étape 2-b : Intégration du composant de gestion de menus

Intégrer un nouveau composant permettant de générer le code des menus Bootstrap plutôt que d’avoir à le coder « en dur ».

Si on simplifie, les menus ne sont, après-tout qu’une arborescence de balises <ul> et <li> contenant des liens vers les routes de l’application.

On va utiliser le bundle « Bootstrap Menu » pour Symfony de Cameron Murphy, qui va faciliter la génération de cette arborescence de balises HTML.

Cette bibliothèque permet d’ajouter assez simplement une définition de menus dans un fichier YAML, présent dans config/packages/bootstrap_menu.yaml et de les utiliser ensuite via la macro render_bootstrap_menu() disponible dans les gabarits Twig.

Cette macro générera une liste à puce (balises <li>) qui seront mises en forme par Bootstrap comme il faut sous forme d’entrées de menus.

Procédez aux étapes suivantes :

  1. Installez le bundle correspondant :

    symfony composer require camurphy/bootstrap-menu-bundle
    
  2. Créez le fichier de configuration config/packages/bootstrap_menu.yaml, en y plaçant ceci, qui définit un menu ’main’ composé d’un seul élément (’todos’) :

    bootstrap_menu:
      menus:
        main:
          items:
            todos:
              label: 'Tasks'
              route: 'todo_list'
            tags:
              label: 'Tags'
              route: 'tag_list'  
    
  3. Modifiez le gabarit templates/base.html.twig pour que la structure des menus de la balise <nav> soit incluse dans un nouveau bloc Twig menu.

    De cette façon, ce même code apparaîtra dans toutes les pages de l’application, sauf si une page surcharge le contenu du block menu.

  4. Supprimez les balises <li> pour les remplacer par l’appel à la macro Twig render_bootstrap_menu().

    Vous devriez obtenir quelque chose du style :

    <body>
    
      {% block menu %}
            <!-- Responsive navbar-->
            <nav class=...
                <div class="container">
                    <a class="navbar-brand" href="#">Todo App</a>
                    <button class="navbar-toggler" ...
                    <div class=...
                        <ul class=...
                            {{ render_bootstrap_menu('main') }}
                        </ul>
                    </div>
                </div>
            </nav>
      {% endblock %}{# menu #}
    
      {% block body %}{% endblock %}
      ...
    

    On a repris ici les indications du README du bundle (installé par composer dans vendor/camurphy/bootstrap-menu-bundle/README.md), pour obtenir le code HTML souhaité.

  5. Exécutez un cache:clear :

    symfony console cache:clear
    
  6. Vous pouvez maintenant tester et vérifier que le menu « Tasks » permet de naviguer vers la route todo_list.

    L’intégration de BootStrap avance bien. Avec son menu, l’application commence à avoir une expérience utilisateur (UX) proche des standards.

    Il reste néanmoins à tirer parti de Bootstrap pour rendre le contenu des pages réellement adapatatif.

4. Étape 3 : Amélioration de la structure de la page d’affichage d’une tâche

Tirer parti des fonctionnalités de la grille Bootstrap pour mettre en œuvre les pages de l’application « fil-rouge ».

Bootstrap fournit des fonctions permettant notamment de modifier l’emplacement des éléments sur la page, pour s’adapter à la place disponible sur les petits écrans.

Vous avez déjà expérimenté cela dans l’étape précédente au niveau du menu de l’application. Nous allons maintenant modifier la structure des pages de l’application afin d’en tirer parti.

4.1. Étape 3-a : Mise en place d’une structure de grille Bootstrap

Modifier le corps des pages HTML de l’application pour intégrer la structure de conteneurs Bootstrap.

Nous allons utiliser la structure de grille de Bootstrap pour mettre en forme le contenu des pages, à la fois pour la présentation générale, mais aussi pour certains détails des propriétés d’une entité.

4.1.1. TODO Modification de la structure du gabarit de base

Vous allez modifier le gabarit de base templates/base.html.twig pour structurer le contenu du bloc Twig body de façon à inclure deux parties : main et footer qui seront deux lignes au sein d’un conteneur Bootstrap.

Ces deux parties seront elles-mêmes des blocs Twig permettant une surcharge pour que chaque gabarit de page n’inclue finalement que la partie utile à insérer au sein de ces deux lignes.

Ces parties sont gérées via des conteneurs définis grâce aux balises HTML <div> et à la classe container de Bootstrap.

Modifiez le gabarit de base pour reprendre la structure suivante, là où auparavant on n’avait qu’un bloc Twig body vide :

...
{% block body %}

  <div class="container body-container">

    <main>

      {# Ici la partie utile que les gabarits des pages vont surcharger #}
      {% block main %}
      <div class="row">
        <div class="col-md-12">
          <p>
            <i>MAIN</i>
          </p>
        </div>
      </div>
      {% endblock %} {# main #}

    </main>

  </div> <!-- /.body-container -->

  {% block footer %}
  <footer class="text-center text-lg-start bg-light text-muted">
    <div class="text-center p-4" style="background-color: rgba(0, 0, 0, 0.05);">
      FOOTER
    </div>
  </footer>
  {% endblock %}{# footer #}


{% endblock %} {# body #}

4.1.2. TODO Test de la nouvelle structure

Testez que cela fonctionne : vous devez voir apparaître « FOOTER » en bas des pages.

Attention : pour que cela fonctionne, il faut que les gabarits des pages du site surchargent bien le nouveau bloc Twig main qu’on vient d’introduire dans le gabarit de base (et pas l’ensemble du block body).

Modifiez par exemple templates/index.html pour utiliser cette nouvelle organisation de la page, en remplaçant le bloc body par un bloc main :

{% extends 'base.html.twig' %}

{% block title %}Welcome!{% endblock %}

{% block main %}

<h1>Welcome</h1>

    <p>{{ welcome }}</p>

{% endblock %} {# main #}

Testez, et vérifiez dans l’outil des gabarits Twig de la barre d’outils de symfony, que l’on a bien la bonne compilation des gabarits, avec une surcharge de main par index.html.twig, à l’intérieur du body de base.html.twig.

Rendering Call Graph

main
└ index.html.twig
│ └ base.html.twig
│   └ index.html.twig::block(title)
│   └ base.html.twig::block(stylesheets)
│   └ base.html.twig::block(menu)
│   │ └ @BootstrapMenu/link.html.twig
│   │ │ └ @BootstrapMenu/href.html.twig
│   │ └ @BootstrapMenu/link.html.twig
│   │   └ @BootstrapMenu/href.html.twig
│   └ base.html.twig::block(body)
│   │ └ index.html.twig::block(main)
│   │ └ base.html.twig::block(footer)
│   └ base.html.twig::block(javascripts)
└ @WebProfiler/Profiler/toolbar_js.html.twig
  └ @WebProfiler/Profiler/toolbar.html.twig
  │ └ @WebProfiler/Profiler/cancel.html.twig::block(toolbar)
  │   └ @WebProfiler/Profiler/toolbar_item.html.twig
  └ @WebProfiler/Profiler/toolbar.css.twig

4.2. Étape 3-b : Application aux pages d’affichage des tâches

Modification des pages de l’application pour utiliser cette nouvelle structure.

4.2.1. TODO Modification de la page de liste des tâches

Remplacez le bloc body par un bloc main dans le gabarit de la page d’affichage de la liste des tâches, templates/todo/index.html.

La différence d’affichage se voit principalement au niveau de l’occupation de toute la largeur de la page, ou de l’affichage seulement sur une portion de la largeur, et sur l’affichage du footer.

Jusque-là, rien de très difficile, mais vous constatez comment on peut relativement simplement modifier tout le contenu d’un site, à partir du moment où la structure d’imbrication des blocs et sous-blocs est bien conçue dans les gabarits de base.

En pratique, sur de « vraies » applications Web, cela nécessitera d’avoir bien réfléchi à l’avance à cette structure, sous peine de devoir retoucher de très nombreux gabarits.

4.2.2. TODO Modification de la page d’affichage d’une tâche

Vous allez maintenant ajuster le contenu de la page d’affichage des tâches, correspondant aux URL de type .../show/N, pour intégrer une structure de type formulaire Bootstrap.

Dans cette page, on utilise la mise en page des formulaires, pour afficher des données en consultation uniquement (on verra les formulaires de saisie dans des séances ultérieures).

  1. Modifiez maintenant le gabarit de la page d’affichage des détails d’une tâche todo/show.html.twig, de façon à ce que le coeur de la page (dans le bloc main), contienne quelque chose du type de ce code Twig, au lieu de la balise <table> :

    <div class="form-horizontal">
    
         <div class="form-group row">
              <label class="col-sm-2 col-form-label">Id</label>
              <div class="col-sm-10">
                      <div class="form-control form-control-plaintext">
                              {{ todo.id }}
                      </div>
              </div>
         </div>
    
         <div class="form-group row">
              <label class="col-sm-2 col-form-label-lg">Libellé</label>
              <div class="col-sm-10">
                      <div class="form-control form-control-plaintext form-control-lg">
                              {{ todo.title }}
                      </div>
              </div>
         </div>
    
         <div class="form-group row">
              <label class="col-sm-2 col-form-label">Terminé</label>
              <div class="col-sm-10">
                      <div class="form-control form-control-plaintext">
                              {{ todo.completed ? "oui" : "non" }}
                      </div>
              </div>
         </div>
    
         <div class="form-group row">
                 <label class="col-sm-2 col-form-label">Created</label>
                 <div class="col-sm-10">
                         <div class="form-control form-control-plaintext">
                                 {{ todo.created ? todo.created|date('Y-m-d H:i:s') : '' }}
                         </div>
                 </div>
         </div>
    
         <div class="form-group row">
                 <label class="col-sm-2 col-form-label">Updated</label>
                 <div class="col-sm-10">
                         <div class="form-control form-control-plaintext">
                                 {{ todo.updated ? todo.updated|date('Y-m-d H:i:s') : '' }}
                         </div>
                 </div>
         </div>
    
    </div> <!-- form-horizontal -->
    
  2. Examinez les détails de l’affichage de la « fiche » qui liste les propriétés d’une tâche. Notez comme on a utilisé une mise en place « tabulaire », avec un « formulaire » en lecture seule, au lieu d’une table HTML :

    • on utilise une section <div>, qui reproduit une présentation de formulaire avec les libellés et leurs valeurs sur la même ligne (cf. « Horizontal form »).
    • Les éléments de la fiche sont mis en page via des lignes row et des largeurs de colonne sur la grille (col-sm-...)
    • Les valeurs des champs (<div class="form-control">) sont rendus en « lecture seule » au lieu d’être des champs de saisie de formulaires, via la classe form-control-plaintext (cf. « Readonly plain text »).

    On manipule différentes balises HTML et leurs propriétés utilisées par le CSS, notamment les classes (class) qui vont servir à Bootstrap à formatter la présentation « formulaire » dans sa grille.

    C’est plutôt complexe à apprendre, mais nous n’avons pas vraiment le temps de détailler tout ça, donc on va procéder par copier/coller, et espérer que ça fonctionne.

  3. Testez le redimensionnement de l’écran, via l’outil de « Vue adaptative », qui permet de faire une prévisualisation en mode petit écran de smartphone, dans les outils du développeur de FireFox. Vous constatez que l’interface reste utilisable, et que la mise en forme initiale évolue, grâce à Bootstrap qui intègre des fonctions d’adaptation du rendu de l’interface.

    Bien évidemment, nous n’avons fait que survoler beaucoup de détails, notamment la façon dont Bootstrap fonctionne « sous le capôt », au niveau CSS et JavaScript… nous n’avons pas le temps de rentrer beaucoup plus loin dans les détails.

  4. Supprimez l’affichage des styles via le menu « Affichage / Style de la page / Aucun style » de Firefox.

    Les infos apparaissent toujours… c’est mieux que rien, et avec un peu de chance, les infos utiles sont accessibles en premier aux utilisateurs de lecteurs d’écran ou d’autres dispositifs d’accessibilité.

L’essentiel à retenir, est que Bootstrap fonctionne sur la base d’une grille, et va prendre des décisions utiles pour gérer au mieux l’affichage du contenu, lorsqu’on devra utiliser l’application avec un navigateur sur un terminal à écran réduit.

Peaufiner l’interface requerrait encore des heures de travail, mais ce ne serait pas raisonable dans le contexte de notre module.

On a compris les grands principes, sur l’utilisation de Bootstrap dans nos gabarits pour intégrer du CSS et un comportement adaptatif. C’est déjà pas mal pour du copier/coller.

5. TODO Étape 4 : Mise en place de Bootstrap dans votre projet

Vous avez maintenant les connaissances nécessaires pour mettre en place Bootstrap dans votre projet, comme nous l’avons fait pour l’application « fil-rouge ».

Poursuivez la séance en déroulant les étapes du guide de réalisation du projet.

Les éléments pour vous guider sur cette étape d’habillage des pages sont identifiés par le label « Après_TP_6 » dans la section du guide de réalisation.

6. Évaluation

Vous avez expérimenté avec les éléments suivants :

  • comment ajouter des feuilles de style CSS et scripts JavaScript dans le code des pages Web, en s’inspirant d’un exemple statique existant
  • l’ajout de composantes de Boostrap avec les balises structurantes <div> pour rendre les pages adaptatives
  • la structure de la grille utilisée par Bootstrap
  • la surcharge de blocs Twig dans les gabarits pour standardiser la structure des pages
  • l’outil d’inspecteur HTML du navigateur, et l’outil de vue adaptative
  • différents utilitaires pour Symfony : asset(), le générateur de menus BootStrap, etc.

Author: Olivier Berger (TSP)

Date: 2024-10-20 Sun 16:08

Emacs (Org mode)