Tutoriel pour découvrir Vue-GWT, le wrapper GWT de Vue.js

Ce tutoriel se propose de présenter le wrapper GWT vue-gwt du framework web Vue.js.

Pour réagir à ce support de cours, un espace de dialogue vous est proposé sur le forum 2 commentaires Donner une note  l'article (5).

Article lu   fois.

Les deux auteur et traducteur

Site personnel

Traducteur : Profil ProSite personnel

Liens sociaux

Viadeo Twitter Facebook Share on Google+   

I. Introduction

Vue.js est, avec Angular et React, un des frameworks JavaScript les plus populaires. Il permet de tirer parti des techniques récentes pour la construction d'applications front-end, et peut se révéler utile pour maintenir des applications qui nécessitent un contenu conséquent de JavaScript et de CSS. Vue.js dispose de nombreux avantages et fonctionnalités, qui sont décrits dans sa documentation.

Dans ce billet, nous voulons nous focaliser sur le wrapper GWT vue-gwt qui est apparu récemment suite à la popularité croissante de Vue.js. Il s'agit là d'un des projets les plus populaires de l'écosystème GWT. Aux fonctionnalités sympathiques de Vue.js, vue-gwt apporte la robustesse de Java et introduit des vérifications lors de la compilation dans plusieurs aspects tels que l'initialisation et le typage des variables. Un des principaux aspects qui a retenu toute notre attention lors des tests de Vue.js est sa simplicité et sa facilité d'apprentissage. Le développeur peut aborder rapidement le cadriciel avec un bagage de connaissances minimales en JavaScript. Nous recommandons que le développeur se familiarise avec Vue.js avant de tenter de développer avec sa variante GWT. Dans la suite du billet, nous allons comparer l'application Vue.js suivante : https://github.com/zak905/vuejs-demo avec sa version GWT : https://github.com/zak905/vuejs-gwt-demo.

II. Vue.js rencontre GWT

L'application de démo est une application de saisie de dépenses en deux composants : un formulaire pour saisir la dépense, et une table pour afficher toutes les dépenses. L'application ressemble à ceci :

Version JavaScript

 
Sélectionnez
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.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
<template>
  <div id="app">
    <img src="./assets/logo.png">
    <div>
      <label for="vat"> VAT in %</label>
      <input id="vat" type="number" v-model="vatRate">
    </div>
    <ExpenseForm :currencies="currencies" :vatRate="vatRate" :expenses="expenses" />
    <div class="divider"></div>
    <ExpenseList :currencies="currencies" :expenses="expenses" />
  </div>
</template>

<script>
import ExpenseForm from './components/ExpenseForm.vue'
import ExpenseList from './components/ExpenseList.vue'

export default {
  name: 'app',
  components: {
    ExpenseForm,
    ExpenseList
  },
   data: () => ({
     vatRate: 20,
     expenses: [],
     currencies: [{name: "EUR", symbol: "?"}, {name: "USD", symbol: "$"}, {name: "GBP", symbol: "£"}],
  }),
}
</script>

<style>
#app {
  font-family: 'Avenir', Helvetica, Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-align: center;
  color: #2c3e50;
  margin-top: 60px;
}
.divider {
  height: 30px;
}
</style>

Version GWT

AppComponent.java

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
@Component(components = {ExpenseFormComponent.class, ExpenseListComponent.class})
public class AppComponent extends VueComponent  {
  @JsProperty
  double vatRate = 20;

  @JsProperty
  List<Expense> expenses = new ArrayList<>();

  @JsProperty
  List<Currency> currencies = Arrays.asList(new Currency("EUR", ""),
                                            new Currency("USD", "$"),
                                            new Currency("GBP", "£"));
}

AppComponent.html

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
<div id="app">
  <img style="width: 10%" src="./assets/logo.png">
  <div>
    <label for="vat"> VAT in %</label>
    <input id="vat" type="number" v-model="vatRate">
  </div>
  <expense-form :currencies="currencies" :vatRate="vatRate" :expenses="expenses" ></expense-form>
  <div class="divider"></div>
  <expense-list :currencies="currencies" :expenses="expenses" :vatRate="vatRate"></expense-list>
</div>

Étant donné que la balise style n'est pas encore supportée dans le fichier .html (surveiller ce ticket pour les mises à jour), tous les styles doivent être inclus dans des fichiers .css séparés pour la version GWT. Pour la balise du nom des composants, Vue.js supporte l'utilisation du nom complet ainsi que sa transformation, tandis que vue-gwt utilise la convention qui dit que chaque composant doit voir son nom se terminer par Component pour chaque fichier .java et .html et, si le composant est inclus en tant qu'enfant dans un autre, il doit être nommé en suivant la notation kebab en retirant le mot Component : le composant ExpenseFormComponent est inclus en tant qu'enfant de AppComponent sous le nom expense-form.

Le composant ExpenseForm ressemble alors à :

Version JavaScript

 
Sélectionnez
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.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
46.
<template>
  <div class="expense-form">
    <label for="amount">Amount: </label>
    <input type="number" id="amount" v-model="amount"/>
    <label for="amountVAT">VAT: </label>
    <input type="number" id="amountVAT" v-model="amountVAT" disabled/>
    <label for="currency">Currency: </label>
    <select id="currency" v-model="currency">
      <option v-for="currency in currencies"> {{ currency.name }} </option>  
    </select>
    <label for="date"  >Date: </label>
    <input type="date" id="date" value="2018/03/06" v-model="date"/>
    <label for="reason">Reason: </label>
    <textarea id="reason" v-model="reason" />
    <button id="append" v-on:click="submitExpense"> Add Expense </button>
  </div>
</template>

<script>
export default {
  name: 'ExpenseForm',
  methods: {
    submitExpense: function(event) {
        this.expenses.push({"amount": this.amount, "date": this.date, "reason": this.reason, "vatRate": this.vatRate, "vat": this.amountVAT, "currency": this.currency});
    }
  },
  data: () => {
      return {
      amount: 0,
      date: '',
      reason: '',
      currency: ''
      }
  },
  props: {
    currencies: Array,
    expenses: Array,
    vatRate: Number,
  },
    computed: {
      amountVAT: function() {return parseFloat(this.vatRate / 100 * this.amount).toFixed(2)}
    }
}
</script>

//..styles etc,..

Version GWT

ExpenseFormComponent.java

 
Sélectionnez
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.
32.
33.
@Component
public class ExpenseFormComponent extends VueComponent {
  @JsProperty
  double amount =  0;
  @JsProperty
  String date = "";
  @JsProperty
  String reason="";
  @JsProperty
  String currency="";

  @Prop
  @JsProperty
  double vatRate;

  @Prop
  @JsProperty
  List<Expense> expenses;

  @Prop
  @JsProperty
  List<Currency> currencies;

  @JsMethod
  public void submitExpense() {
    expenses.add(new Expense(amount, date, reason, getAmountVAT(), vatRate, currency));
  }

  @Computed
  public double getAmountVAT() {
    return vatRate / 100 * amount;
  }
}

ExpenseForm.html

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
<vue-gwt:import class="com.gwidgets.client.dto.Currency"/>
<div class="expense-form">
    <label for="amount">Amount: </label>
    <input type="number" id="amount" v-model="amount"/>
    <label for="amountVAT">VAT: </label>
    <input type="number" id="amountVAT" v-model="amountVAT" disabled/>
        <label for="currency">Currency: </label>
    <select id="currency" v-model="currency">
            <option v-for="Currency currency in currencies"> {{ currency.getName() }} </option>
        </select>
    <label for="date"  >Date: </label>
    <input type="date" id="date" value="2018/03/06" v-model="date"/>
    <label for="reason">Reason: </label>
    <textarea id="reason" v-model="reason" />
    <button id="append" v-on:click="submitExpense"> Add Expense </button>
</div>

La principale différence entre les deux versions est le renforcement du typage :

 
Sélectionnez
1.
2.
3.
  <select id="currency" v-model="currency">
    <option v-for="Currency currency in currencies"> {{ currency.getName() }} </option>
  </select>

Cela nécessite une balise d'importation spéciale : <vue-gwt:import class="com.gwidgets.client.dto.Currency"/>

La liste des dépenses ressemble à ceci :

Version JavaScript

 
Sélectionnez
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.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
<template>
  <div class="expense-list">
      <table class="expense-table">
          <thead>
              <th>Amount</th>
              <th>Date</th>
              <th>VAT rate %</th>
              <th>VAT</th>
              <th>Reason</th>
          </thead>
          <tbody>
              <tr v-for="expense in expenses">
                  <td>{{ expense.amount + getCurrencySymbol(expense.currency, currencies)}}</td>
                  <td>{{ expense.date }}</td>
                  <td>{{ expense.vatRate }}</td>
                  <td>{{ expense.vat + getCurrencySymbol(expense.currency, currencies)}}</td>
                  <td>{{ expense.reason }}</td>
              </tr>

          </tbody>
      </table>
  </div>
</template>

<script>
export default {
  name: 'ExpenseList',
  props: {
    currencies: Array,
    expenses: Array,
  },
  methods: {
      getCurrencySymbol: (currencyName, currencies) => {
            for (let i = 0; i < currencies.length; i++ ) {
            if (currencyName === currencies[i].name)
               return currencies[i].symbol;
          }  
          return "$"
          }
  }
}
</script>

//..styles etc

Version GWT

ExpenseListComponent.java

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
@Component
public class ExpenseListComponent extends VueComponent {
  @Prop
  @JsProperty
  double vatRate;

  @Prop
  @JsProperty
  List<Expense> expenses;

  @Prop
  @JsProperty
  List<Currency> currencies;

  @JsMethod
  public String getCurrencySymbol(String currencyName) {
    return currencies.stream()
                     .filter(currency -> currency.getName().equals(currencyName))
                     .findFirst()
                     .map(Currency::getSymbol)
                     .orElse("$");
  }
}

EExpenseListComponent.html

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
<vue-gwt:import class="com.gwidgets.client.dto.Expense"/>
<div class="expense-list">
    <table class="expense-table">
        <thead>
        <th>Amount</th>
        <th>Date</th>
        <th>VAT rate %</th>
        <th>VAT</th>
        <th>Reason</th>
        </thead>
        <tbody>
        <tr v-for="Expense expense in expenses">
            <td>{{ expense.amount + getCurrencySymbol(expense.currency)}}</td>
            <td>{{ expense.date }}</td>
            <td>{{ expense.vatRate }}</td>
            <td>{{ expense.amountVAT + getCurrencySymbol(expense.currency)}}</td>
            <td>{{ expense.reason }}</td>
        </tr>
        </tbody>
    </table>
</div>

Une fois de plus, GWT requiert que le type Expense soit explicité.

III. Que gagne-t-on à utiliser GWT par-dessus Vue.js

Au premier coup d'œil, il semble que vue-gwt ait ajouté pas mal de vérifications qui renforcent l'intégrité des données et évite de déployer en production une application bancale. Entre autres, lors de la compilation, vue-gwt va vérifier l'initialisation des variables. Supposons que nous affichons des listes ou des tables, et que nous oublions la variable dans le v-for. Par exemple, supprimons le champ currencies de AppComponent.java. Avec vue-gwt, vous obtiendrez une erreur de compilation et vous ne pourrez pas construire votre application :

 
Sélectionnez
1.
[17,8] In AppComponent.html at line 7: Couldn't find variable/method "currencies". Make sure you didn't forget the @JsProperty/@JsMethod annotation.

Dans la version JavaScript, l'application peut être construite et déployée malgré l'absence de currencies dans les données. Un message sera bien affiché dans la console en mode développeur, mais bien sûr c'est trop tard lorsque l'application est déjà déployée. Sur de petites applications comme celle-ci, les effets sont minimes, mais les répercussions sont bien plus déplaisantes sur de grosses applications.

Un autre avantage à utiliser vue-gwt est de tirer parti des API Java telles que : Collections, Optional, Stream (les flux), les tables de hachage, etc. À partir de sa version 2.8.0, GWT supporte les flux et les expressions lambda, et cela peut vous simplifier la mise lorsque vous travaillez sur des transformations et des traitements de données complexes. Par exemple, dans le composant ExpenselistComponent, nous avons utilisé les API de flux de données pour filtrer les objets en fonction de la monnaie :

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
  @JsMethod
  public String getCurrencySymbol(String currencyName) {
    return currencies.stream()
                     .filter(currency -> currency.getName().equals(currencyName))
                     .findFirst()
                     .map(Currency::getSymbol)
                     .orElse("$");
  }

Pour être tout à fait franc, il existe aussi une API de flux de données en JavaScript ; cependant, tous les navigateurs ne la supportent pas encore.

Enfin, vue-gwt renforce la vérification du typage dans les modèles comme nous l'avons mentionné plus tôt : <tr v-for="Expense expense in expenses">, ce qui permet de rendre les applications encore plus robustes et résilientes aux changements apportés aux objets de données.

IV. Conclusion

Vue.js est un framework JavaScript proéminent, et, accompagné de vue-gwt, les développeurs pourront en tirer le maximum. vue-gwt introduit une nouvelle manière de développer des applications GWT en utilisant des modèles .html et des classes .java correspondantes, et le résultat final sera une application Vue.js robuste basée sur du Java. GWT a rencontré Vue.js et il semble qu'il en soit tombé sous le charme.

V. Remerciements

Cet article a été publié avec l'aimable autorisation de Zakaria Amine. L'article original est disponible à cette adresse : http://www.g-widgets.com/2018/03/24/a-walk-through-the-gwt-wrapper-for-vue-js-vue-gwt/.

Nous tenons à remercier f-leb pour la relecture orthographique attentive de cet article, Fabrice Bouyé pour la traduction en langue française et Mickael Baron pour la mise au gabarit.

Vous avez aimé ce tutoriel ? Alors partagez-le en cliquant sur les boutons suivants : Viadeo Twitter Facebook Share on Google+   

  

Les sources présentées sur cette page sont libres de droits et vous pouvez les utiliser à votre convenance. Par contre, la page de présentation constitue une œuvre intellectuelle protégée par les droits d'auteur. Copyright © 2018 Zakaria Amine. Aucune reproduction, même partielle, ne peut être faite de ce site et de l'ensemble de son contenu : textes, documents, images, etc. sans l'autorisation expresse de l'auteur. Sinon vous encourez selon la loi jusqu'à trois ans de prison et jusqu'à 300 000 € de dommages et intérêts.