Échange de données entre plusieurs composants

Bonjour à tous,

Je suis nouveau dans la communauté vueJS, j’ai deja fait quelques tutos et comprends les principes de base mais je cale sur un problème.

J’ai pour projet de développer une petite application pour une équipe enfants de football. Cette application permettrait d’avoir 2 listes (terrain et réserve) dans un composant et un chronomètre dans un autre composant. Le but étant à la fin de sortir une liste des entrées, sorties, goal et le temps pour chaque événement.

J’ai réussi à faire mes 2 composants distincts. Le chrono fonctionne et le composant avec les 2 listes aussi. Si je clique sur sortie, le joueur change bien de “liste”, idem sur entrée.

J’arrive à a voir dans le console.log “entrée de Jonas”, “Sortie de Luis”, etc mais je n’arrive pas à le coupler avec le temps.
Ex: 3:22 - Sortie de Noa
3:54 : Entrée de Martin

Mon code est en composant mono fichiers.
J’ai donc dans mon app.vue un composant chrono et un composant PlayerDrag, comment accéder depuis mon composant PlayerDrag aux datas du composant Chrono.

App.vue

<template>
 <div id="app">
   <Chrono></Chrono>
   <PlayerDrag> </PlayerDrag>
 </div>
</template>

<script>
import PlayerDrag from './components/PlayerDrag.vue'
import Chrono from './components/Chrono.vue'

export default {
 name: 'app',
 components: {
   PlayerDrag,
   Chrono
 }
}
</script> 

Chrono.vue

<template>
  <div>
    <h1>Le chronomètre</h1>
    <hr />
    <h1>
      <span class="label label-primary">{{minutes}}</span> :
      <span class="label label-primary">{{secondes}}</span> 
    </h1>
    <br />
    <p>
      <button type="button" class="btn btn-primary" @click="startChrono">Lancer</button>
      <button type="button" class="btn btn-primary"  @click="stopChrono">Stop</button>
    </p>
  </div>
</template>
<script>
export default {
  name: "Chrono",
  display: "Chrono",

  data() {
    return {
      minutes: 0,
      secondes: 0,
      totalSecondes: 0, 
      timer:''
    };
  },

  methods: {

    startChrono: function() {
      var v = this;
      v.timer = setInterval(function() {
      v.minutes = Math.floor(++v.totalSecondes / 60);
      v.secondes = v.totalSecondes - v.minutes * 60;
        }
      }, 1000);
    },

    stopChrono: function () {
      clearInterval(this.timer);
    }
  },
};
</script>

PlayerDrag.vue

<template>
  <div>
    <div class="container">
      <div class="row">
        <div class="col">
          <div class="card">
            <div class="card-body">
              <ul class="list-group">
                    <li v-for="(joueur,idx) in terrain" :key="joueur.id" class="list-group-item">
                      {{joueur.name}}
                      <span>
                        <button
                          type="button"
                          class="btn btn-primary btn-sm"
                          @click="goal(joueur.id)"
                        >Goal</button>
                        <button
                          type="button"
                          class="btn btn-secondary btn-sm"
                          @click.prevent="sortie(idx,joueur.id)"
                        >Sortie</button>
                      </span>
                    </li>
              </ul>
            </div>
          </div>
        </div>
        <div class="col">
          <div class="card">
            <div class="card-body">
              <ul class="list-group">
                    <li v-for="(joueur,idx) in banc" :key="joueur.id" class="list-group-item">
                      {{joueur.name}}
                      <span>
                        <button
                          type="button"
                          class="btn btn-secondary btn-sm"
                          @click.prevent="entree(idx,joueur.id)"
                        >Entree</button>
                      </span>
                    </li>
              </ul>
            </div>
          </div>
        </div>
      </div>
    </div>
  </div>
</template>

<script>


export default {
  name: "PlayerDrag",

  data() {
    return {
      terrain: [
        { name: "Audric", id: 11 },
        { name: "Jonas", id: 21 },
        { name: "Martin", id: 31 },
        { name: "Luis", id: 40 },
        { name: "Noa", id: 50 }
      ],
      banc: [],
      test:[],
      info: null
    };
  },
 
  methods: {

    goal: function(id) {
         var playerObj = this.terrain.filter(function(elem) {
        if (elem.id == id) return elem.name;
      });
      window.console.log("Goal de " + playerObj[0].name);
    },

    sortie: function(idx, id) {
      var playerObj = this.terrain.filter(function(elem) {
        if (elem.id == id) return elem.name;
      });
     
      this.terrain.splice(idx, 1);
      this.banc.push({ name: playerObj[0].name, id });
      window.console.log("Sortie de "+playerObj[0].name)
    },

    entree: function(idx, id) {
      var playerObj = this.banc.filter(function(elem) {
        if (elem.id == id) return elem.name;
      });
      this.banc.splice(idx, 1);
      this.terrain.push({ name: playerObj[0].name, id });
    },
  }
};
</script>

Est-ce possible? sinon quelle serait la méthode à suivre pour faire ce genre d’application.
Merci de votre aide
Guillaume

Bonjour,

Dans votre App.vue, vous pouvez y definir vos datas (timer, people)
Ainsi, vous pourrais passer ses données en ̀props` de vos components.

Pour mettre à jour les jeux de données dans votre component, utilisez un watcher.

Merci NewQuery,
Je vais regarder à cela alors.Donc si je comprends bien definir mes datas plutot en global et passer en props les données. Désolé je viens du php, j’apprends seulement vuejs et c’est pas tjs simple lol

Je vois trois solutions :

  1. Chrono.vue envoie à son parent le chrono que tu veux afficher
watch: {
  secondes: 'updateParent',
  minutes: 'updateParent'
},
methods: {
  updateParent () {
    this.$emit('update', `${this.minutes}:${this.seconds}`
  }
}

dans App.vue, une variable time

<Chrono @update="time = $event" />
<PlayerDrag :time="time" />
    • avantage: le composant Chrono garde la responsabilité du temps
    • inconvénient: il y a une variable intermédiaire “sale” dans App.vue
  1. déplacer seconds/minutes/timer de Chrono.vue vers App.vue (comme l’a suggéré @newQuery)
    et dans App.vue :
<PlayerDrag :time="time" />
...
computed: {
   time () { return `${this.minutes}:${this.seconds}` }
}
    • avantage: plus de variable intermédiaire “sale”
    • inconvénient: le composant Chrono n’est plus responsable du temps
  1. seule solution propre pour partager des données entre plusieurs components sans lien de parenté : utiliser vuex

    • avantage : pas de variable intermédiaire, les variables du chronometre sont isolées (ne vont pas être mélangées avec d’autres variables de App.vue ou autre (voir les modules de vuex))
    • inconvénient : il faut connaitre vuex / encore un truc de plus à apprendre pour toi

Matxx, Merci pour tes exemples. J’étais parti sur la solution 2, cela fonctionne pour le moment :slight_smile:
Je mets de coté ta solution 3 pour le moment, car j’avoue que je n’ai pas trop facile avec Vue. Apres 10j de tests et d’apprentissage, cela va déja mieux, je compte même coupler avec Laravel (Axios) le “log” des entrées/sorties des joueurs avec le temps pour le stocker en base de données. Mais chaque chose en son temps.
Merci encore pour ton aide
Guillaume