Tutoriel VueJS 3 – Composants et props

Dans cette leçon, nous allons nous pencher sur le concept des composants et des props de Vue.js.

Notre objectif

Refaites l’application d’exemple pour utiliser un composant de produit, qui utilise un prop.

Blocs de construction d'une application Vue

Dans les frameworks JavaScript frontaux modernes, les composants sont les éléments constitutifs d’une application, et c’est certainement le cas avec Vue. Vous pouvez imaginer les composants un peu comme des Legos que vous pouvez emboîter les uns dans les autres dans une hiérarchie d’arbre généalogique de composants.

Une page Web donnée peut être composée de plusieurs composants, et il est courant que des composants soient des « parents » dans lesquels sont imbriqués des composants enfants.

Création de notre premier composant

Entrons dans notre application et créons notre premier composant. Comme notre application aura éventuellement plusieurs composants, nous allons créer un dossier components, dans lequel nous allons créer notre premier composant, appelé ProductDisplay.js.

La syntaxe pour créer un composant ressemble à ceci :

components/ProductDisplay.js
app.component('product-display', {})

Le premier argument est le nom du composant, 'product-display' et le second argument est un objet permettant de configurer notre composant (similaire à l’objet options utilisé pour configurer notre application Vue).

Template

Parce que nous avons besoin que notre composant ait une structure, nous allons ajouter la propriété template et coller tout le code HTML basé sur le produit qui se trouve actuellement dans index.html, dans un template literal.

components/ProductDisplay.js
app.component('product-display', {
    template:
        `<div class="product-display">
      <div class="product-container">
        <div class="product-image">
          <img v-bind:src="image">
        </div>
        <div class="product-info">
          <h1>{{ title }}</h1>
  
          <p v-if="inStock">En stock</p>
          <p v-else>En rupture</p>
  
          <div class="variants-wrapper">
              <div 
                v-for="(variant, index) in variants" 
                :key="variant.id" 
                @mouseover="updateVariant(index)" 
                class="color-circle" 
                :style="{ backgroundColor: variant.color }">
              </div>
          </div>
          
          <button 
            class="button" 
            :class="{ disabledButton: !inStock }" 
            :disabled="!inStock" 
            v-on:click="addToCart">
            Ajouter au panier
          </button>
        </div>
      </div>
    </div>`
})

Notez que nous n’avons pas modifié ce code, nous le déplaçons simplement dans le composant product-display afin qu’il y soit encapsulé.

Données et méthodes

Maintenant que nous avons donné à ce composant son modèle, ou sa structure, nous devons lui donner ses données et ses méthodes, qui se trouvent toujours dans main.js. Nous allons donc les coller maintenant :

components/ProductDisplay.js
app.component('product-display', {
    template:
        `<div class="product-display">
       ...
    </div>`,
    data() {
        return {
            product: 'T-Shirt',
            brand: 'Gekkode',
            selectedVariant: 0,
            details: ['60% coton', '30% laine', '10% polyester'],
            variants: [
                { id: 2234, color: '#0000FF', image: './assets/images/t-shirt-bleu.png', quantity: 20 },
                { id: 2235, color: '#FF0000', image: './assets/images/t-shirt-rouge.png', quantity: 0 }
            ]
        }
    },
    methods: {
        addToCart() {
            this.cart += 1
        },
        updateVariant(index) {
            this.selectedVariant = index
        }
    },
    computed: {
        title() {
            return this.brand + ' ' + this.product
        },
        image() {
            return this.variants[this.selectedVariant].image
        },
        inStock() {
            return this.variants[this.selectedVariant].quantity
        }
    }
})

Nous nous assurerons de supprimer le panier des données ici car nous n’avons pas besoin que chaque produit ait son propre panier.

Nettoyage de main.js

Maintenant que nous avons encapsulé tout ce code spécifique au produit dans notre composant product-display, nous pouvons nettoyer notre fichier main.js.

main.js
const app = Vue.createApp({
  data() {
    return {
      cart: 0,
    }
  },
  methods: {}
})

Nous avons laissé le panier et l’option des méthodes parce qu’il y aura une nouvelle méthode plus tard.

Importation du composant

Afin d’utiliser product-display, nous devons l’importer dans notre index.html.

index.html
<!-- Importer le composant -->
<script src="./components/ProductDisplay.js"></script>

Maintenant qu’il est importé, nous pouvons l’utiliser dans notre modèle.

index.html
<div id="app">
  <div class="nav-bar"></div>
  <div class="cart">Cart({{ cart }})</div>
  <product-display></product-display>
</div>

Si nous vérifions cela dans un navigateur, nous verrons que tout s’affiche comme avant, mais depuis que nous avons réorganisé les choses, le bouton Ajouter au panier n’incrémente plus le panier. Nous corrigerons cela dans la prochaine leçon.

Pour l’instant, afin de vous montrer à quel point ces blocs de code réutilisables peuvent être utiles, je vais ajouter deux autres composants d’affichage de produits.

index.html
<div id="app">
  <div class="nav-bar"></div>

  <div class="cart">Cart({{ cart }})</div>
  <product-display></product-display>
  <product-display></product-display>
  <product-display></product-display>
</div>

Lorsque nous rafraîchissons le navigateur, nous les voyons tous apparaître. Chacun d’entre eux est fonctionnel de manière indépendante.

Props

Maintenant que nous commençons à apprendre comment encapsuler du code réutilisable dans ces composants, que se passe-t-il lorsque notre composant a besoin de quelque chose qui est en dehors de lui-même ? Par exemple, que se passe-t-il si le parent, pour ainsi dire, a des données de message et que l’enfant en a besoin ? Parce qu’un composant a sa propre portée isolée, c’est à dire qu’il ne peut pas atteindre les données extérieur à lui-même pour saisir quelque chose qui est en dehors de sa portée.

La réponse ici c’est les props. Il s’agit d’attributs personnalisés permettant de transmettre des données à un composant. Ils fonctionnent un peu comme un entonnoir, dans lequel vous pouvez faire passer les données dont le composant a besoin.

Ajoutons la possibilité pour notre composant de recevoir un prop.

Donner un prop à notre composant

Donnons à notre application Vue à la racine, située dans main.js, une nouvelle propriété de données, qui indique si l’utilisateur est un utilisateur premium ou non.

main.js
const app = Vue.createApp({
  data() {
    return {
      cart: 0,
      premium: true
    }
  }
})

Si un utilisateur est premium, son expédition sera gratuite. Notre composant product-display doit donc avoir accès à ces données. En d’autres termes, il a besoin d’un attribut personnalisé (un entonnoir) dans lequel nous pouvons introduire ces données. Ajoutons-le maintenant, ce que nous ferons en donnant au composant une option props, et en lui ajoutant un prop premium.

components/ProductDisplay.js
app.component('product-display', {
  props: {
    premium: {
      type: Boolean,
      required: true
    }
  },
  ...
}

Remarquez que la fonction props de Vue dispose d’une validation intégrée, ce qui nous permet de spécifier des éléments tels que le type de prop, son caractère obligatoire, etc.

Maintenant que nous avons configuré cela, nous pouvons ajouter cet attribut personnalisé au composant product-display où nous l’utilisons.

index.html
<div id="app">
  <div class="nav-bar"></div>

  <div class="cart">Cart({{ cart }})</div>
  <product-display :premium="premium"></product-display>
</div>

Remarquez que nous utilisons le raccourci pour v-bind afin de recevoir de manière réactive la nouvelle valeur de premium si elle est mise à jour (de true à false).

Utilisation du props

Maintenant que notre composant d’affichage de produit possède l’accessoire premium, nous pouvons l’utiliser dans le composant. Rappelez-vous, nous voulons utiliser le fait qu’un utilisateur soit premium ou non pour déterminer ce qu’il doit payer pour l’expédition.

Dans le modèle du composant, nous allons ajouter :

components/ProductDisplay.js
template: 
  `<div class="product-display">
    ...
      <p>Livraison: {{ shipping }}</p>
    ...
  </div>`,

Ici, shipping est le nom d’une nouvelle propriété calculée sur le composant product-display, qui ressemble à ceci :

components/ProductDisplay.js
computed: {
  ...
  shipping() {
    if (this.premium) {
      return 'Gratuit'
    }
      return "2,99€"
  }
}

La propriété calculée vérifie si la proposition de prime est vraie, et si c’est le cas, elle renvoie « Gratuit ». Sinon, elle renvoie « 2,99€ »

Ressources

Pensez à télécharger le code de départ pour commencer cette étape du tutoriel dans de bonnes conditions.

Newsletter

Ne manquez jamais les nouveaux conseils, tutoriels et autres.

Pas de spam, jamais. Nous ne partagerons jamais votre adresse électronique et vous pouvez vous désabonner à tout moment.