Laravel 7: une application SPA avec Vue.js

Cet article décrit les premières étapes pour afficher des ressources protégées dans une page Vue.JS, dans le cadre de la mise en place d’une application avec une seule page Web (SPA ou Single Page Application).

Le point de départ de l’article est un projet Laravel 7 disposant déjà d’une connexion à une base de données relationnelle et de Modèles Eloquent sur les tables (voir l’article du blog Microtuto: Docker: créer un conteneur pour un projet Laravel 7 sous Windows 10).

Références:

Prérequis: installation de Nodes.js et NPM

Référence: procédure d’installation de Nodes.js sur O2Switch.

Installation de Vues.JS et des écrans de gestion de l’authentification

Une seule commande Artisan permet de générer les éléments de base frontend Laravel / UI avec le framework javacript Vue.js, et la gestion de d’authentification « classique » à partir des identifiants et mot de passe des utilisateurs enregistrés dans la table users : formulaire d’enregistrement, d’authentification, Mot de passe oublié… :

php artisan ui vue --auth

Laravel utilise NPM – le gestionnaire de packages javascript de Nodes.JS – pour installer les librairies frontend dont Vue.JS. Il faudra charger et compiler les librairies après chaque modification du frontend, en particulier d’un Composant de Vues.js en lancant la commande suivante à la racine du projet Laravel dans une console (console linux ou invite de commandes Windows, ou encore fenêtre Powershell):

npm install && npm run dev

Alternativement, pour lancer le moniteur et compiler prendre en compte immédiatement les changements sur le code Vue.JS il est plus simple de lancer le watcher NPM (utiliser la combinaison de touche Ctrl+C pour l’arrêter):

npm run watch

Outre la mise à jour des fichiers javascript et css de l’application Laravel (app.js et app.css), l’installation du package Laravel / UI crée les fichiers suivants dans le dossier: resources/js:

components/ExampleComponent.vue: modèle de Composant. Un Composant Vue.JS est composé:

  • D’un template qui est un bloc HTML avec du code Vue.JS
  • D’une partie Script qui contient du code Javascript spécifique au composant.

js/app.js: fichiers javacript de l’application Vue.JS dans lequel sont déclarés les composants, par exemple le composant « example-component ». Nous remarquons ici l’objet javascript « Vue » qui déclaré comme une variable globale dans le fichier javascript du projet Laravel, et qui permet de créer les instances Vues comme on le verra plus loin:

Vue.component('example-component', require('./components/ExampleComponent.vue').default);

Par ailleurs, l’installation de Laravel UI crée également une vue Laravel Blade home.blade.php affichant les menu Login et Register (tous les écrans nécessaires à la gestion de l’authentification sont par ailleurs disponibles):

vue Laravel avec le menu d'authentification
http://localhost/home – vue Laravel home.blade.php

En complément si besoin, les étapes pour afficher la vue ci-dessus sur l’URL:

http://localhost/home

1/ Définir une route Laravel associant cette URL à la méthode ‘home’ du contrôleur HomeController, dans le fichier Laravel web.php:

Route::get('/home', 'HomeController@home')->name('home');

Dans le contrôleur HomeController, la méthode ‘home’ renvoie simplement la vue home.blade.php:

public function home()
{
    return view('home');
}

Afficher un composant Vue.JS dans une vue Laravel Blade

Remarque: avant d’afficher une page utilisant Vue.js, il est intéressant d’installer le plugin « Vue Devtools » dans son navigateur: le plugin est disponible sous Chrome, Firefox ou comme application standalone. Sur les navigateurs, il est affiché comme un onglet supplémentaire dans les outils de développements Web.

Un attribut id est présent sur le tout premier bloc HTML du fichier layouts/app.blade.php, qui est utilisé dans le fichier Blade home.blade.php sur lequel on souhaite associer une instance Vue:

<body>
    <div id="app">
    ...

On va créer une instance de Vue et l’associer à ce bloc, à l’aide de l’objet global Vue dans le fichier resources/js/app.js:

const app = new Vue({
    el: '#app'
});

L’attribut « el » permet de spécifierdans la page HTML l’élément du DOM (Document Object Model) qui portera la nouvelle Vue: ici il s’agit de l’élément dont l’id est égal à « app ». Ce bloc est le composant Vue « Root ».

Pour ajouter à ce composant « Root » un autre Composant il faut simplement ajouter un tag HTML dans la vue Laravel Blade: home.blade.php, correspondant au nom du composant tel qu’il est déclaré dans app.js:

<div class="card">
   <example-component></example-component>
</div>

L’affichage de la page: http://localhost/home intègre maintenant les 2 Composants Vue.js: « Root » et « example-component »:

Affichage d'un composant Vue.js dans une Vue Laravel Blade
Composant Vue.js « example-component » avec l’affichage du plugin Devtools

Lier un composant Vue avec des données dynamiques

L’objectif d’un outil frontend comme Vue.js consiste surtout à lier la couche de présentation HTML aux données, qui sont représentées en général sous forme d’objets javascritp JSON (récupérés depuis l’API du backend de l’applicatino Laravel).

On va ensuite modifier la déclaration de l’instance Vue dans le fichier resources/js/app.js, pour y ajouter des données (attribut « data »):

const app = new Vue({
    el: '#app',
    data: { message: 'Mon message'}
});

A ce stade (et après recompilation des sources javascript par la commande « npm install && npm run dev »), on peut afficher la donnée dans la page Blade: home.blade.php, en utilisant la même syntaxe que dans Blade (double accolades):

<div class="card">
   @{{ test }}
   <example-component></example-component> 
</div>

Attention: On note l’utilisation du caractère d’échappement Blade « @ », qui évite l’affichage d’un message d’erreur « Use of undefined constant message« : car ici on affiche la variable « message » dans une vue Laravel Blade et cette n’est pas reconnue par Blade.

Vue Blade affichant la variable Vue.js: message
Vue Blade affichant la variable Vue.js: message

Si l’on souhaite maintenant afficher cette même variable message dans le composant Vue.js: « example-component », il est alors nécessaire de le passer dans le scope de ce conteneur à l’aide d’un attribut ajouté au tag « example-component »:

<div class="card">
   <example-component :message="message"></example-component> 
</div>

Par ailleurs, il faut modifier le fichier du composant lui-même: ExempleComponent.vue, afin d’ajout deux éléments:

  • dans la partie « template », l’affichage de la variable {{ message }} (sans le @ de l’exemple précédent car nous ne sommes plus dans une vue Blade)
  • dans la partie « script », la déclaration de la propriété attendue par le composant (avec le type attendu).

Ci-dessous le contenu du fichier ExempleComponent.vue:

<template>
    <div class="container">
        ...
              <div class="card-body">
                 {{ message }}
              </div>
        ...
    </div>
</template>

<script>
    export default {
        mounted() {
            console.log('Component mounted.')
        },
        props: {
            message: String
        }
    }
</script>
Composant Vue affichant une variable passée depuis le Composant Root
Composant Vue affichant une variable passée depuis le Composant Root

Récupérer et afficher des instances Eloquent dans un Composant Vue.JS

L’étape suivante consiste à récupérer des enregistrements depuis la base de données et de les afficher dans un Composant Vue. Cela nécessite de réaliser un appel depuis le composant Root Vue vers une API exposée par un controlleur Laravel.

La table utilisée ici est la table users créée précédemment lors de l’installation du package Laravel UI avec l’authentification. Les colonnes suivantes seront affichées: user.name, user.mail, user.created_at (date de création).

Création de l’API: user-index

Dans un premier temps, on crée un nouveau Controlleur Laravel, qui ne contiendra que les méthodes en lecture:

  • index(): récupération de tous les utilisateurs
  • show(): récupération d’un utilisateur à partir de son identifiant

Remarque: dans un premier temps, cette API n’est pas sécurisés, ce sera l’objet de l’étape suivante avec l’installation du package Passport.

La méthode show() tient en une seule ligne, et renvoie tous les enregistrements de la table users sous forme de liste JSON:

public function index()
{
    return response()->json(User::all());
}

Il faut ensuite définir la route permettant de récupérer les utilisateurs à partie d’un appel REST depuis le frontend Vue.js. Le appels REST sont définis dans le fichier routes/api.php, et sont ainsi directement considérés comme des appels d’api, utilisant le préfixe « /api » comme préfixe lors des appel (par exemple: http://localhost/api/users pour appeler la méthode index).

La définition utilise la méthode Route:ressource pour définir l’api REST? mais nous spécifions ici que nous n’exposeront que les méthodes index et show:

Route::resource('users', 'UserController')->only([
    'index', 'show'
]);

On peut tester directement la méthode depuis un navigateur avec l’appel suivante:

http://localhost/api/users

Appel sur l'API REST dans le navigateur - réponse JSON
Appel sur l’API REST dans le navigateur – réponse JSON

On constate au passage que la colonne Password n’est pas renvoyée, ce qui est bien sûr préférable et s’explique par son inclusion dans la liste des champs cachés (« hidden ») dans le modèle Eloquent User.php:

protected $hidden = [
    'password', 'remember_token',
];

Appel sur l’API REST et affichage dans le frontend Vue.js

L’appel à l’API doit être codé dans la configuration de l’instance Vue définie dans le fichier app.js, sur le paramètre « created »:

const app = new Vue({
    el: '#app',
    data: {users: []},
    created: function() {
        // Alias the component instance as `vm`, so that we
        // can access it inside the promise function
        var vm = this;
        // Fetch our array of posts from an API
        fetch("api/users")
            .then(function(response) {
                return response.json();
            })
            .then(function(data) {
                vm.users = data;
            });
    }
});

Le résultat de l’appel est affecté sur la donnée « users ». Cette dernière est inclue dans le Composant Root, puis passé au composant « ExampleComponent » inclu dans le fichier home.blade.php:

<div class="card">
    <example-component :users="users"></example-component>
</div>

La valeur « users » est définie comme propriété du Composant dans la partie Script de ce dernier, et affiché dans une liste sur la partie Template. Voici le contenu du fichier: ExampleComponent.php:

<template>
    <div class="card">
        ...
                <ol>
                    <li v-for="user in users" :key="user.id">
                        {{ user.name }} [<a :href="'mailto:' + user.email ">{{ user.email }}</a>], créé le {{ user.created_at }}
                    </li>
                </ol>
      ...
    </div>
</template>

<script>
    export default {
        mounted() {
            console.log('ExampleComponent mounted.')
        },
        props: {
            users: Array
        }
    }
</script>

On peut à nouveau afficher la page Home pour voir la liste des utilisateurs présentée dansle composant ExampleComponent:

http://localhost/home

Composant affichant la liste des utilisateurs
Le Composant ExampleComponent affiche la liste des utilisateurs

Conclusion

Nous avons mis en place les fondations d’une application SPA (Single Page Application) avec un backend Laravel exposant une API et un frontend Vue.js consommant cette API.

Quelques références pour travailler sur Vue.js (avec Axios pour les appels d’API et Bootstrap Vue qui est installé par défaut):

Laisser un commentaire

Votre adresse de messagerie ne sera pas publiée. Les champs obligatoires sont indiqués avec *