→ 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 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
→ Vue.js pour les débutants leçon 10: formulaires
Le but de la leçon
Nous voulons avoir des onglets sur la page de l'application, dont l'un permet aux visiteurs de rédiger des avis sur les produits et l'autre leur permet de consulter les avis existants.
Code initial
Voici Ă quoi ressemble le contenu du fichier Ă cette Ă©tape du travail
index.html
:
<div id="app">
<div class="cart">
<p>Cart({{ cart.length }})</p>
</div>
<product :premium="premium" @add-to-cart="updateCart"></product>
</div>
Il
main.js
y a le code suivant:
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, index) in details" :key="index">{{ 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
@click="addToCart"
:disabled="!inStock"
:class="{ disabledButton: !inStock }"
>
Add to cart
</button>
</div>
<div>
<h2><font color="#3AC1EF">Reviews</font></h2>
<p v-if="!reviews.length">There are no reviews yet.</p>
<ul>
<li v-for="review in reviews">
<p>{{ review.name }}</p>
<p>Rating: {{ review.rating }}</p>
<p>{{ review.review }}</p>
</li>
</ul>
</div>
<product-review @review-submitted="addReview"></product-review>
</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
}
],
reviews: []
}
},
methods: {
addToCart() {
this.$emit('add-to-cart', this.variants[this.selectedVariant].variantId);
},
updateProduct(index) {
this.selectedVariant = index;
},
addReview(productReview) {
this.reviews.push(productReview)
}
},
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
}
}
}
})
Vue.component('product-review', {
template: `
<form class="review-form" @submit.prevent="onSubmit">
<p v-if="errors.length">
<b>Please correct the following error(s):</b>
<ul>
<li v-for="error in errors">{{ error }}</li>
</ul>
</p>
<p>
<label for="name">Name:</label>
<input id="name" v-model="name">
</p>
<p>
<label for="review">Review:</label>
<textarea id="review" v-model="review"></textarea>
</p>
<p>
<label for="rating">Rating:</label>
<select id="rating" v-model.number="rating">
<option>5</option>
<option>4</option>
<option>3</option>
<option>2</option>
<option>1</option>
</select>
</p>
<p>
<input type="submit" value="Submit">
</p>
</form>
`,
data() {
return {
name: null,
review: null,
rating: null,
errors: []
}
},
methods: {
onSubmit() {
if(this.name && this.review && this.rating) {
let productReview = {
name: this.name,
review: this.review,
rating: this.rating
}
this.$emit('review-submitted', productReview)
this.name = null
this.review = null
this.rating = null
} else {
if(!this.name) this.errors.push("Name required.")
if(!this.review) this.errors.push("Review required.")
if(!this.rating) this.errors.push("Rating required.")
}
}
}
})
var app = new Vue({
el: '#app',
data: {
premium: true,
cart: []
},
methods: {
updateCart(id) {
this.cart.push(id);
}
}
})
Voici Ă quoi ressemble l'application maintenant.
Page d'application
Tâche
Actuellement, les avis et le formulaire utilisé pour soumettre des avis sont affichés l'un à côté de l'autre sur la page. C'est tout à fait une structure de travail. Mais de plus en plus d'avis devraient apparaître sur la page au fil du temps. Cela signifie qu'il sera plus pratique pour les utilisateurs d'interagir avec une page qui, de leur choix, affiche soit un formulaire, soit une liste d'avis.
La solution du problème
Afin de résoudre notre problème, nous pouvons ajouter un système d'onglets à la page. L'un d'eux, avec un titre
Reviews
, affichera des critiques. Le second, avec un titre Make a Review
, affichera un formulaire de soumission d'avis.
Création d'un composant qui implémente le système d'onglets
Commençons par créer un composant
product-tabs
. Il sera affiché en bas de la représentation visuelle du composant product
. Au fil du temps, il remplacera le code actuellement utilisé pour afficher la liste des avis et des formulaires sur la page.
Vue.component('product-tabs', {
template: `
<div>
<span class="tab" v-for="(tab, index) in tabs" :key="index">{{ tab }}</span>
</div>
`,
data() {
return {
tabs: ['Reviews', 'Make a Review']
}
}
})
Pour le moment, il ne s'agit que d'un composant vierge que nous finaliserons bientôt. Pour l'instant, discutons brièvement de ce qui est présenté dans ce code.
Les données du composant ont un tableau
tabs
contenant les chaînes que nous utilisons comme en-têtes d'onglet. Le modèle de composant utilise une construction v-for
qui tabs
crée un élément <span>
contenant la chaîne correspondante pour chaque élément du tableau . Ce qui forme ce composant à ce stade de travail ressemblera à celui illustré ci-dessous.
Le composant onglets de produit au stade initial de son travail
. Pour atteindre nos objectifs, nous devons savoir lequel des onglets est actif. Par conséquent, ajoutons une propriété aux données du composant
selectedTab
. Nous définirons dynamiquement la valeur de cette propriété à l'aide d'un gestionnaire d'événements qui répond aux clics sur les titres des onglets:
@click="selectedTab = tab"
La propriété contiendra des chaînes correspondant aux en-têtes des onglets.
Autrement dit, si l'utilisateur clique sur l'onglet
Reviews
, selectedTab
une chaîne sera écrite Reviews
. Si vous cliquez sur l'onglet Make a Review
, la selectedTab
ligne sera incluse Make a Review
.
Voici Ă quoi ressemblera le code complet du composant maintenant.
Vue.component('product-tabs', {
template: `
<div>
<ul>
<span class="tab"
v-for="(tab, index) in tabs"
@click="selectedTab = tab"
>{{ tab }}</span>
</ul>
</div>
`,
data() {
return {
tabs: ['Reviews', 'Make a Review'],
selectedTab: 'Reviews' // @click
}
}
})
Lier une classe Ă un onglet actif
Un utilisateur travaillant avec une interface qui utilise des onglets doit savoir quel onglet est actif. Vous pouvez implémenter un mécanisme similaire en utilisant la liaison de classe aux éléments
<span>
utilisés pour afficher les noms des onglets:
:class="{ activeTab: selectedTab === tab }"
Voici le fichier CSS qui définit le style de la classe utilisée ici
activeTab
. Voici Ă quoi ressemble ce style:
.activeTab {
color: #16C0B0;
text-decoration: underline;
}
Et voici le style de classe
tab
:
.tab {
margin-left: 20px;
cursor: pointer;
}
Si vous expliquez la construction ci-dessus dans un langage simple, il s'avère que le style spécifié pour la classe est appliqué à l'onglet
activeTab
, dans le cas oĂą il selectedTab
est Ă©gal tab
. Puisque le selectedTab
nom de l'onglet sur lequel l'utilisateur vient de cliquer est Ă©crit, le style .activeTab
sera appliqué spécifiquement à l'onglet actif.
En d'autres termes, lorsque l'utilisateur clique sur le premier onglet,
tab
sera localisé Reviews
, le mĂŞme sera Ă©crit selectedTab
. En conséquence, le style sera appliqué au premier onglet .activeTab
.
Maintenant, les titres des onglets sur la page ressembleront Ă ceux ci-dessous.
Le titre en surbrillance de l'onglet actif Il
semble que tout fonctionne comme prévu à ce stade, nous pouvons donc passer à autre chose.
Travailler sur le modèle de composant
Maintenant que nous pouvons dire à l'utilisateur quel onglet est actif, nous pouvons continuer à travailler sur le composant. À savoir, nous parlons de finaliser son modèle, décrivant ce qui sera exactement affiché sur la page lorsque chacun des onglets sera activé.
Pensons à ce qui devrait être montré à l'utilisateur s'il clique sur l'onglet
Reviews
. Il s'agit bien sûr de critiques de produits. Par conséquent, déplaçons le code d'affichage des critiques du modèle de composant product
vers le modèle de composant product-tabs
, en plaçant ce code sous la construction utilisée pour afficher les en-têtes d'onglets. Voici à quoi ressemblera le modèle de composant maintenant product-tabs
:
template: `
<div>
<ul>
<span class="tab"
:class="{ activeTab: selectedTab === tab }"
v-for="(tab, index) in tabs"
@click="selectedTab = tab"
>{{ tab }}</span>
</ul>
<div>
<p v-if="!reviews.length">There are no reviews yet.</p>
<ul>
<li v-for="review in reviews">
<p>{{ review.name }}</p>
<p>Rating: {{ review.rating }}</p>
<p>{{ review.review }}</p>
</li>
</ul>
</div>
</div>
`
Notez que nous nous sommes débarrassés de la balise
<h2><font color="#3AC1EF">
car nous n'avons plus besoin d'afficher le titre Reviews
au-dessus de la liste des avis. Au lieu de ce titre, le titre de l'onglet correspondant sera affiché.
Mais déplacer le code du modèle seul ne suffit pas pour fournir des commentaires. Le tableau
reviews
dont les données sont utilisées pour afficher les avis est stocké dans le cadre des données du composant product
. Nous devons transmettre ce tableau au composant en product-tabs
utilisant le mécanisme des accessoires du composant . Ajoutons ce qui product-tabs
suit à l'objet avec les options utilisées lors de la création :
props: {
reviews: {
type: Array,
required: false
}
}
Passons un tableau
reviews
de composant product
Ă composant en product-tabs
utilisant la product
construction suivante dans le modèle :
<product-tabs :reviews="reviews"></product-tabs>
Pensons maintenant à ce qui doit être affiché sur la page si l'utilisateur clique sur le titre de l'onglet
Make a Review
. Ceci est, bien sûr, un formulaire pour soumettre des commentaires. Afin de préparer le projet à un travail ultérieur, transférons le code product-review
de connexion du composant du modèle de composant product
au modèle product-tabs
. Plaçons le code suivant sous l'élément <div>
utilisé pour afficher la liste des avis:
<div>
<product-review @review-submitted="addReview"></product-review>
</div>
Si vous regardez la page de candidature maintenant, vous constaterez que la liste des avis et le formulaire y sont affichés sous les en-têtes des onglets.
Une étape intermédiaire de travail sur la page
Dans ce cas, les clics sur les titres, bien qu'ils conduisent à leur sélection, n'affectent en rien les autres éléments de la page. De plus, si vous essayez d'utiliser le formulaire, il s'avère qu'il a cessé de fonctionner normalement. Tous ces éléments sont des conséquences tout à fait attendues des modifications que nous avons apportées à l'application. Continuons à travailler et mettons notre projet en état de marche.
Affichage conditionnel des éléments de page
Maintenant que nous avons préparé les principaux éléments du modèle de composant
product-tabs
, il est temps de créer un système qui vous permettra d'afficher différents éléments de page en fonction du titre de l'onglet sur lequel l'utilisateur a cliqué.
Les données du composant ont déjà une propriété
selectedTab
. Nous pouvons l'utiliser dans une directive v-show
pour rendre conditionnellement ce qui appartient Ă chacun des onglets.
Ainsi, Ă la balise
<div>
contenant le code de génération de la liste des avis, nous pouvons ajouter la construction suivante:
v-show="selectedTab === 'Reviews'"
Grâce à elle, la liste des avis s'affichera lorsque l'onglet sera actif
Reviews
.
De mĂŞme, nous ajouterons ce qui suit Ă la balise
<div>
qui contient le code de connexion du composant product-review
:
v-show="selectedTab === 'Make a Review'"
Cela entraînera l'affichage du formulaire uniquement lorsque l'onglet est actif
Make a Review
.
Voici à quoi ressemblera le modèle de composant maintenant
product-tabs
:
template: `
<div>
<ul>
<span class="tab"
:class="{ activeTab: selectedTab === tab }"
v-for="(tab, index) in tabs"
@click="selectedTab = tab"
>{{ tab }}</span>
</ul>
<div v-show="selectedTab === 'Reviews'">
<p v-if="!reviews.length">There are no reviews yet.</p>
<ul>
<li v-for="review in reviews">
<p>{{ review.name }}</p>
<p>Rating: {{ review.rating }}</p>
<p>{{ review.review }}</p>
</li>
</ul>
</div>
<div v-show="selectedTab === 'Make a Review'">
<product-review @review-submitted="addReview"></product-review>
</div>
</div>
`
Si vous regardez la page et cliquez sur les onglets, vous pouvez vous assurer que le mécanisme que nous avons créé fonctionne correctement.
Un clic sur les onglets masque certains éléments et en affiche d'autres. L'
envoi de commentaires via un formulaire ne fonctionne toujours pas. Examinons le problème et résolvons-le.
Résoudre le problème de l'envoi de commentaires
Si vous regardez maintenant la console des outils de développement du navigateur, vous pouvez voir un avertissement.
Avertissement de la console
De toute évidence, le système ne peut pas détecter la méthode
addReview
. Que lui est-il arrivé?
Pour répondre à cette question, rappelez-vous qu'il
addReview
s'agit d'une méthode déclarée dans un composantproduct
. Il doit être appelé si le composantproduct-review
(et c'est un composant enfant du composantproduct
) génère un événementreview-submitted
:
<product-review @review-submitted="addReview"></product-review>
C'est ainsi que tout fonctionnait avant de transférer l'extrait de code ci-dessus vers le composant
product-tabs
. Et maintenant, un composant product
est un composant enfant product-tabs
, et product-review
maintenant ce n'est pas un «enfant», un composant product
, mais son «petit-fils».
Notre code est maintenant conçu pour interagir
product-review
avec le composant parent. Mais maintenant, ce n'est plus un composant product
. En conséquence, il s'avère que nous devons refactoriser le code du projet pour que le formulaire fonctionne correctement.
Refactoring du code du projet
Afin d'assurer la communication des composants petits-enfants avec leurs «grands-parents», ou afin d'établir une communication entre des composants de même niveau, un mécanisme appelé bus d'événements global est souvent utilisé.
Le Global Event Bus est un canal de communication qui peut être utilisé pour transférer des informations entre les composants. Et c'est, en fait, juste une instance de Vue qui est créée sans lui passer un objet avec des options. Créons un bus d'événements:
var eventBus = new Vue()
Ce code ira au niveau supérieur du fichier
main.js
.
Vous trouverez peut-être plus facile de comprendre ce concept si vous considérez le bus événementiel comme un bus. Ses passagers sont des données que certains composants envoient à d'autres. Dans notre cas, nous parlons de transférer des informations sur les événements générés par d'autres composants vers un composant. Autrement dit, notre «bus» voyagera d'un composant
product-review
Ă l'autre product
, transportant les informations que le formulaire a été soumis et livrant les données du formulaire de product-review
Ă product
.
Maintenant dans le composant
product-review
, dans la méthode onSubmit
, il y a une ligne comme celle-ci:
this.$emit('review-submitted', productReview)
Nous le remplacerons par ce qui suit, en utilisant Ă la
eventBus
place this
:
eventBus.$emit('review-submitted', productReview)
Après cela, vous n'avez plus besoin d'écouter l'événement du
review-submitted
composant product-review
. Par conséquent, nous allons changer le code de ce composant dans le modèle de composant product-tabs
comme suit:
<product-review></product-review>
La
product
méthode peut maintenant être supprimée du composant addReview
. Au lieu de cela, nous utiliserons la construction suivante:
eventBus.$on('review-submitted', productReview => {
this.reviews.push(productReview)
})
Nous parlerons ci-dessous de la façon de l'utiliser dans un composant, mais pour l'instant, nous allons décrire en quelques mots ce qui s'y passe. Cette construction indique que lorsqu'elle
eventBus
génère un événement review-submitted
, vous devez prendre les données passées dans cet événement (c'est-à -dire - productReview
) et les placer dans le tableau de reviews
composants product
. En fait, c'est très similaire à ce qui a été fait jusqu'à présent dans une méthode addReview
dont nous n'avons plus besoin. Notez que l'extrait de code ci-dessus utilise une fonction de flèche. Ce moment mérite une couverture plus détaillée.
Raisons d'utiliser une fonction fléchée
Ici, nous utilisons la syntaxe de la fonction de flèche qui a été introduite dans ES6. Le fait est que le contexte de la fonction de flèche est lié au contexte parent. Autrement dit, lorsque nous, à l'intérieur de cette fonction, utilisons un mot-clé
this
, il Ă©quivaut au mot this
- clé qui correspond à l'entité contenant la fonction flèche.
Ce code peut être réécrit sans utiliser les fonctions fléchées, mais vous devez ensuite organiser la liaison
this
:
eventBus.$on('review-submitted', function (productReview) {
this.reviews.push(productReview)
}.bind(this))
Terminer le projet
Nous avons presque atteint notre objectif. Il ne reste plus qu'à trouver une place pour le morceau de code qui apporte une réponse à l'événement
review-submitted
. Une product
fonction peut devenir une telle place dans un composant mounted
:
mounted() {
eventBus.$on('review-submitted', productReview => {
this.reviews.push(productReview)
})
}
Quelle est cette fonction? Il s'agit d'un hook de cycle de vie qui est appelé une fois après le montage du composant dans le DOM. Maintenant, une fois le composant
product
monté, il attendra que les événements se produisent review-submitted
. Après la génération d'un tel événement, ce qui est passé dans cet événement sera ajouté aux données du composant, c'est-à -dire - productReview
.
Si vous essayez maintenant de laisser un avis sur le produit en utilisant le formulaire, il s'avère que cet avis est affiché là où il devrait être.
Le formulaire fonctionne comme il se doit
Le bus d'événements n'est pas la meilleure solution pour communiquer des composants
Bien que le bus d'événements soit souvent utilisé, et bien que vous puissiez le trouver dans divers projets, gardez à l'esprit que c'est loin d'être la meilleure solution au problème de la connexion des composants d'application.
Au fur et à mesure que l'application se développe, un système de gestion d'état basé sur Vuex peut s'avérer très utile . Il s'agit d'un modèle et d'une bibliothèque de gestion de l'état des applications.
Atelier
Ajoutez des onglets
Shipping
et au projet Details
, qui, respectivement, affichent le coût de livraison des achats et des informations sur les marchandises.
- 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 dans ce didacticiel:
- Vous pouvez utiliser des outils de rendu conditionnel pour organiser le mécanisme des onglets.
- , Vue, .
- — . . — Vuex.
Nous espérons qu'après avoir suivi ce cours Vue, vous avez appris ce que vous vouliez et êtes prêt à apprendre beaucoup plus de choses nouvelles et intéressantes sur ce framework.
Si vous venez de terminer ce cours, partagez vos impressions.
→ 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: Gestion des événements
→ Vue.js pour les débutants, Leçon 6: Classes et styles de liaison
→ Vue.js pour les débutants, Leçon 7: Propriétés calculées
→Vue.js pour les débutants, leçon 8: Composants
→ Vue.js pour les débutants, leçon 9: événements personnalisés
→ Vue.js pour les débutants, leçon 10: Formulaires