Vue.js2 : avec Laravel

Comme ce blog est essentiellement consacré à Laravel il me faut évoquer l’utilisation de Vue.js 2 avec Laravel 5.3 puisque cette version a été enrichie de tout ce qu’il faut pour sceller ce mariage. J’en ai un peu parlé dans mon article sur les nouveautés de la version 5.3. je vais y revenir de façon plus précise maintenant en poursuivant l’exemple du panier dans le contexte de Laravel.

Etat des lieux

Dans l’article référencé ci-dessus j’avais présenté l’infrastructure en place lorsqu’on installe Laravel 5.3 :

img31

On a déjà :

  • un composant vue.js 2 : Exemple.vue
  • une application de base : app.js
  • un fichier d’initialisation : bootstrap.js

Avec Laravel on n’utilise pas directement Webpack, même si on pourrait si on le voulait, mais plutôt Elixir qui est lui-même une surcouche qui simplifie l’utilisation de Gulp.

Si on regarde le fichier de configuration de Gulp (gulpfile.js) :

const elixir = require('laravel-elixir');

require('laravel-elixir-vue-2');

/*
 |--------------------------------------------------------------------------
 | Elixir Asset Management
 |--------------------------------------------------------------------------
 |
 | Elixir provides a clean, fluent API for defining some basic Gulp tasks
 | for your Laravel application. By default, we are compiling the Sass
 | file for our application, as well as publishing vendor resources.
 |
 */

elixir(mix => {
    mix.sass('app.scss')
       .webpack('app.js');
});

On voit qu’on lance une tâche Webpack en référençant le fichier app.js qui est l’entrée de l’application. C’est donc Elixir qui se charge de tout de façon invisible (on a pas de fichier webpack.config.js mais on peut en créer un au besoin).

Concernant npm on a ce fichier package.json :

{
  "private": true,
  "scripts": {
    "prod": "gulp --production",
    "dev": "gulp watch"
  },
  "devDependencies": {
    "bootstrap-sass": "^3.3.7",
    "gulp": "^3.9.1",
    "jquery": "^3.1.0",
    "laravel-elixir": "^6.0.0-11",
    "laravel-elixir-vue-2": "^0.2.0",
    "laravel-elixir-webpack-official": "^1.0.2",
    "lodash": "^4.16.2",
    "vue": "^2.0.1",
    "vue-resource": "^1.0.3"
  }
}

On voit qu’on charge vue ainsi que le plugin vue-resource dont je parlerai dans un autre article.

On dispose aussi de deux scripts :

  • dev : pour le développement avec une observation des modification (watch)
  • prod : pour la production en compressant les fichiers

Installation

La première chose à faire est évidemment d’utiliser npm pour charger toutes les dépendances :

npm install

Ce qui crée le dossier node_modules avec tous les fichiers :

img35

On sait qu’on aura besoin du système de template pug pour le panier, alors on l’ajoute :

npm install pug

On a ainsi tout ce qu’il faut au niveau des dépendances !

En faisant mes essais je me suis rendu compte que la transformation du code ES6 en ES5 avec Elixir n’était pas assurée par Babel mais par Bublé. Cela a certaines conséquences parce que ce n’est pas l’ensemble de ES6 qui est pris en charge, ce qui est justifié dans la documentation :

Bublé limits itself to ES features that can be compiled to compact, performant ES5

Vous disposez d’ailleurs d’une page avec les fonctionnalités non supportées. En particulier on y trouve le for…of que j’ai utilisé dans le précédent article pour justement tester la transformation effectuée par Babel. C’est d’ailleurs comme ça que je m’en suis rendu compte, parce que je tombais sur une erreur et que j’ai dû un peu fouiller parce que rien n’est indiqué dans la documentation de Laravel…

On peut tout de même changer les options de fonctionnement de Bublé mais pas d’une façon qui m’a semblé suffisamment claire et adaptable à la situation. On peut évidemment utiliser Babel mais le loader est codé en dur dans Elixir…

Pour le présent article j’ai pris la configuration par défaut et enlevé le for…of du code, ce qui était le plus simple !

Le panier

Maintenant pour mettre en place le panier c’est tout simple. On crée une route pour le tester :

Route::get('test', function () {
    return view('test');
});

La vue test.blade.php :

<!DOCTYPE html>
<html lang="fr">
  <head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>Test vue.js</title>
    <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css">
    <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/font-awesome/4.6.3/css/font-awesome.min.css">
    <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootswatch/3.3.7/united/bootstrap.min.css">
  </head>
  <body>
    <div class="container">
        <br>
        <div id="app">
          <app></app>          
        </div>
    </div>
    <script src="/js/app.js"></script>
  </body>
</html>

On a :

  • un identifiant app pour servir de référence à Vue.js
  • une balise app pour notre application
  • le chargement du fichier JavaScript de l’application /js/app.js

Dans app.js on change le composant :

require('./bootstrap');

Vue.component('app', require('./components/App.vue'));

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

On met en place nos trois composants et on supprime Exemple.vue :

img36

Pour rappel voici le code de App.vue :

<template>
  <div id="app">
    <panier :panier="panier"></panier> 
  </div>
</template>

<script>
import Panier from './Panier.vue'

export default {
  name: 'application',
  data () {
    return {
      panier: [
        { article: "Cahier", quantite: 2, prix: '5.30' },
        { article: "Crayon", quantite: 4, prix: '1.10' },
        { article: "Gomme", quantite: 1, prix: '3.25' }
      ]
    }
  },
  components: {
    Panier
  }
}
</script>

Le code de Panier.vue :

<template>
<div class="panel panel-primary">
  <div class="panel-heading">Panier</div>        
  <table class="table table-bordered table-striped">
    <thead>
      <tr>
       <th class="col-sm-4">Article</th>
       <th class="col-sm-2">Quantité</th>
       <th class="col-sm-2">Prix</th>
       <th class="col-sm-2">Total</th>
       <th class="col-sm-1"></th>
       <th class="col-sm-1"></th>
      </tr>
    </thead>
    <tbody>
      <tr v-for="(item, index) in panier">
        <td>{{ item.article }}</td>
        <td>{{ item.quantite }}</td> 
        <td>{{ item.prix }} €</td>
        <td>{{ (item.quantite * item.prix).toFixed(2) }} €</td>
        <td><button class="btn btn-info btn-block" @click="modifier(index)"><i class="fa fa-edit fa-lg"></i></button></td>
        <td><button class="btn btn-danger btn-block" @click="supprimer(index)"><i class="fa fa-trash-o fa-lg"></i></button></td>
      </tr> 
      <tr>
        <td colspan="3"></td>
        <td><strong>{{ total }} €</strong></td>
        <td colspan="2"></td>
      </tr> 
      <editeur :article="article" @add="ajouter"></editeur>
    </tbody>       
  </table>
</div>
</template>

<script>
import Editeur from './Editeur.vue'

export default {
  props: ['panier'],
  data: function () {
    return {
      article: { article: '', quantite: 0, prix: 0 }
    }
  },
  computed: {
    total: function () {
      let total = 0;
      this.panier.forEach(function(el) {
          total += el.prix * el.quantite;
      });
      return total.toFixed(2)
    }
  },
  methods: {
    modifier: function(index) {
      this.article = this.panier[index]
      this.panier.splice(index, 1)
    },
    supprimer: function(index) {
      this.panier.splice(index, 1)
    },
    ajouter: function(input) {
      this.panier.push(input)
      this.article = { article: '', quantite: 0, prix: 0 }
    }
  },
  components: {
    Editeur
  }
}
</script>

C’est là que j’ai supprimé le for…of.

Et Editeur.vue :

<template lang="pug">
tr(class="bleu")
  td
    input(type="text" class="form-control" v-model="input.article" ref="modif" placeholder="Article")
  td
    input(type="text" class="form-control" v-model="input.quantite" placeholder="Quantité")
  td
    input(type="text" class="form-control" v-model="input.prix" placeholder="Prix")
  td(colspan="3")
    button(class="btn btn-primary btn-block" @click="ajouter()") "Ajouter"
</template>

<script>
export default {
  props: ['article'],
  computed: {
    input: function() {
      return this.article;
    }
  },
  methods: {
    ajouter: function() {
      this.$emit('add', this.input);
    }
  }  
}
</script>

<style lang="sass">
$fond: lightblue;
.bleu {
  background-color: $fond !important;
}
</style>

On lance Gulp

Il ne reste plus qu’à voir si ça fonctionne :

img37

Apparemment tout se passe bien en mode développement. On retrouve bien le panier :

img32

Et on est en mode watch pour que tous les changements soient pris en compte.

Et voilà ce que ça donne en mode production :

img38

Cette fois le fichier /js/app.js est minifié et le panier fonctionne encore !

On voit donc que la mise en oeuvre de Vue.js2 avec Laravel 5.3 est assez facile.

Reste toutefois à voir s’il est aussi facile de modifier la configuration selon les besoins. Je n’en ai pas l’expérience parce que je n’ai eu aucun projet qui justifie ce genre de mise en oeuvre.

Laisser un commentaire