Vue.js pour les débutants, leçon 9: événements personnalisés

Dans la leçon précédente de notre cours Vue, vous avez appris à créer des composants et à transmettre des données d'entités parentes aux enfants à l'aide du mécanisme des accessoires. Et si les données doivent être transférées dans la direction opposée? Aujourd'hui, dans la neuvième leçon, vous apprendrez à établir une communication bidirectionnelle entre des composants de différents niveaux.







Vue.js pour les débutants Leçon 1: Instance de Vue

Vue.js pour les débutants, Leçon 2: Liaison d'attributs

Vue.js pour les débutants, Leçon 3: Rendu conditionnel

Vue.js pour les débutants, Leçon 4: Listes de rendu

Vue .js pour les débutants leçon 5: traitement des événements

Vue.js pour débutants leçon 6: lier les classes et les styles

Vue.js pour débutants leçon 7: propriétés calculées

Vue.js pour débutants leçon 8: composants

Vue. js pour les débutants leçon 9: événements personnalisés



Le but de la leçon



Nous voulons que le composant productpuisse dire à l'entité parente, l'instance racine de Vue, qu'un événement s'est produit. Dans ce cas, il productdoit envoyer, avec la notification de l'occurrence de l'événement, certaines données.



Code initial



L'exemple de fichier de index.htmlprojet contient désormais le code suivant:



<div id="app">
  <product :premium="premium"></product>
</div>


Voici le contenu du fichier main.js:



Vue.component('product', {
  props: {
    premium: {
      type: Boolean,
      required: true
    }
  },
  template: `
  <div class="product">
    <div class="product-image">
      <img :src="image" />
    </div>

    <div class="product-info">
      <h1>{{ title }}</h1>
      <p v-if="inStock">In stock</p>
      <p v-else>Out of Stock</p>
      <p>Shipping: {{ shipping }}</p>

      <ul>
        <li v-for="detail in details">{{ detail }}</li>
      </ul>
      <div
        class="color-box"
        v-for="(variant, index) in variants"
        :key="variant.variantId"
        :style="{ backgroundColor: variant.variantColor }"
        @mouseover="updateProduct(index)"
      ></div>

      <button
        v-on:click="addToCart"
        :disabled="!inStock"
        :class="{ disabledButton: !inStock }"
      >
        Add to cart
      </button>

      <div class="cart">
        <p>Cart({{ cart }})</p>
      </div>
    </div>
  </div>
  `,
  data() {
    return {
      product: 'Socks',
      brand: 'Vue Mastery',
      selectedVariant: 0,
      details: ['80% cotton', '20% polyester', 'Gender-neutral'],
      variants: [
        {
          variantId: 2234,
          variantColor: 'green',
          variantImage: './assets/vmSocks-green.jpg',
          variantQuantity: 10
        },
        {
          variantId: 2235,
          variantColor: 'blue',
          variantImage: './assets/vmSocks-blue.jpg',
          variantQuantity: 0
        }
      ],
      cart: 0,
    }
  },
    methods: {
      addToCart() {
        this.cart += 1;
      },
      updateProduct(index) {
        this.selectedVariant = index;
        console.log(index);
      }
    },
    computed: {
      title() {
        return this.brand + ' ' + this.product;
      },
      image() {
        return this.variants[this.selectedVariant].variantImage;
      },
      inStock() {
        return this.variants[this.selectedVariant].variantQuantity;
      },
      shipping() {
        if (this.premium) {
          return "Free";
        } else {
          return 2.99
        }
      }
    }
})

var app = new Vue({
  el: '#app',
  data: {
    premium: true
  }
})


Tâche



Maintenant qu'il productest présenté comme un composant autonome, cela productn'a aucun sens que le code lié au panier y soit . Si chaque produit a son propre panier dont nous devons surveiller l'état, notre application deviendra un gros désordre. Au lieu de cela, nous aimerions que le panier existe à l'instance racine de Vue. Nous avons également besoin du composant pour productinformer l'instance racine de Vue sur l'ajout d'articles au panier, c'est-à-dire sur les clics sur le bouton Add to cart.



Décision



Déplaçons les données liées au panier vers l'instance racine de Vue:



var app = new Vue({
  el: '#app',
  data: {
    premium: true,
    cart: 0
  }
})


Ensuite, déplaçons le modèle de panier vers index.html, en apportant son code à ce formulaire:



<div id="app">
  <div class="cart">
    <p>Cart({{ cart }})</p>
  </div>

  <product :premium="premium"></product>
</div>


Maintenant, si vous ouvrez la page de l'application dans un navigateur et cliquez sur le bouton Add to cart, rien ne se passe comme prévu.





Cliquer sur le bouton Ajouter au panier n'aboutit encore à rien



. Que se passe-t-il lorsque vous cliquez sur ce bouton? Nous avons besoin qu'en cliquant dessus, l'instance racine de Vue reçoive une notification qui appellerait une méthode qui met à jour le panier, c'est-à-dire met à jour la valeur stockée danscart.



Pour y parvenir, réécrivons d'abord le code de la méthode duaddToCartcomposantproduct.



Maintenant, cela ressemble à ceci:



addToCart() {
  this.cart += 1;
},


Amenons-le sous cette forme:



addToCart() {
  this.$emit('add-to-cart');
},


Qu'est-ce que tout cela signifie?



Voilà donc ce que c'est. Lorsque la méthode est appelée addToCart, un événement nommé personnalisé est généré add-to-cart. En d'autres termes, lorsque le bouton Add to cartest cliqué, une méthode est appelée qui génère un événement indiquant que le bouton vient d'être pressé (c'est-à-dire que l'événement déclenché par le clic du bouton vient de se produire).



Mais pour le moment, rien dans l'application n'attend que cet événement se produise ou ne l'écoute. Ajoutons un écouteur d'événement à index.html:



<product :premium="premium" @add-to-cart="updateCart"></product>


Ici, nous utilisons la construction de vue de la @add-to-cardmême manière que nous utilisons la construction :premium. Mais s'il :premiums'agit d'un "pipeline" à travers lequel des données peuvent être transmises au composant enfant depuis le parent, alors @add-to-cartil peut être comparé au "récepteur radio" du composant parent, qui reçoit des informations du composant enfant selon lesquelles un bouton a été enfoncé Add to cart. Puisque la "radio" est dans une balise <product>imbriquée <div id="app">, cela signifie que lorsque des informations sur un clic arrivent , la Add to cartméthode updateCartsituée dans l'instance racine de Vue sera appelée . Traduit en langage ordinaire, le



code @add-to-cart=«updateCart»ressemble à ceci: "Lorsque vous entendez qu'un événement s'est produit add-to-cart, appelez la méthode updateCart."



Cette méthode, qui sera maintenant déclarée dans l'objet options utilisé lors de l'instanciation de Vue, vous l'avez probablement vue quelque part:



methods: {
  updateCart() {
    this.cart += 1;
  }
}


En fait, c'est exactement la même méthode qui a été utilisée plus tôt dans product. Mais maintenant, il se trouve dans l'instance racine de Vue et est appelé en cliquant sur un bouton Add to cart.





Le bouton fonctionne à nouveau



Lorsque vous cliquez sur un bouton qui se trouve dans un composantproduct, une méthode est appeléeaddToCartqui génère un événement. L'instance racine de Vue, écoutant la radio, apprend que l'événement s'est produit et appelle une méthodeupdateCartqui incrémente le nombre stocké danscart.



Nous avons atteint notre objectif, mais dans une application réelle, savoir qu'un événement s'est produit, qu'un certain produit a été ajouté au panier, n'apportera pas beaucoup d'avantages. En réalité, vous devez au moins savoir quel produit a été ajouté au panier. Cela signifie que dans l'événement généré en réponse à une pression sur le bouton, vous devez également transférer certaines données.



Les données passées dans l'événement peuvent être décrites comme le deuxième argument passé$emitdans le code de la méthode duaddToCartcomposantproduct:



this.$emit('add-to-cart', this.variants[this.selectedVariant].variantId);


Maintenant, l'événement transmet l'identifiant ( variantId) du produit que l'utilisateur souhaite ajouter au panier. Cela signifie qu'au lieu de simplement augmenter le nombre d'articles dans le panier, nous pouvons aller plus loin et stocker des informations plus détaillées sur les articles qui y sont ajoutés dans le panier. Pour ce faire, nous convertissons d'abord le panier en tableau en écrivant un tableau vide dans cart:



cart: []


Ensuite, réécrivons la méthode updateCart. Premièrement - il acceptera maintenant id- le même identifiant de produit qui est maintenant passé dans l'événement, et deuxièmement - il mettra maintenant ce qu'il a reçu dans un tableau:



methods: {
  updateCart(id) {
    this.cart.push(id);
  }
}


Après un simple clic sur le bouton, l'identifiant du produit est ajouté au tableau. Le tableau est affiché sur la page.





La baie avec l'ID de produit est affichée sur la page.



Nous n'avons pas besoin d'afficher la baie entière sur la page. Nous serons satisfaits de la sortie du nombre de produits ajoutés au panier, c'est-à-dire au tableaucart. Par conséquent, nous pouvons réécrire le code de l'étiquette<p>, qui affiche des informations sur le nombre de produits ajoutés au panier, comme ceci:



<p>Cart({{ cart.length }})</p>




La page affiche des informations sur le nombre d'articles ajoutés au panier.



Maintenant, nous affichons simplement la longueur du tableau sur la page, ou, en d'autres termes, le nombre d'articles dans le panier. Extérieurement, le panier a le même aspect qu'avant, mais maintenant, au lieu d'augmenter simplement la valeur d'une propriété numériquecart, nous stockons dans un tableau descartinformations sur l'article qui a été ajouté au panier.



Atelier



Ajoutez un bouton au projet qui supprime le cartproduit ajouté précédemment du tableau . En cliquant sur ce bouton, un événement doit être généré contenant des informations sur l'identifiant de l'article à retirer du panier.



  • Voici un modèle que vous pouvez utiliser pour résoudre ce problème.
  • Voici la solution au problème.


Résultat



Voici ce que vous avez appris aujourd'hui:



  • Un composant peut informer l'entité parente que quelque chose s'est passé en lui en utilisant la construction $emit.
  • Un composant parent peut utiliser un gestionnaire d'événements défini à l'aide de la directive v-on(ou de sa version abrégée @) pour organiser une réponse aux événements générés par les composants enfants. Si un événement se produit, un gestionnaire d'événements peut être appelé dans le composant parent.
  • , , .


Si vous étudiez le cours et que vous avez atteint cette leçon, veuillez nous indiquer le but pour lequel vous faites, ce que vous voulez réaliser en maîtrisant Vue.



Vue.js débutants leçon 1: instance Vue

Vue.js pour les débutants, leçon 2: attributs de liaison

Vue.js débutants leçon 3: rendu conditionnel

Vue.js débutants leçon 4: rendu des listes

Vue .js pour les débutants leçon 5: traitement des événements

Vue.js débutants leçon 6: classes et styles de liaison

Vue.js débutants leçon 7: propriétés calculées

Vue.js débutants leçon 8: composants

Vue. js pour les débutants leçon 9: événements personnalisés






All Articles