Partage
  • Partager sur Facebook
  • Partager sur Twitter

v-model dans un boucle

vueJs

    11 mai 2022 à 13:43:31

    Bonjour,
    J'arrive à la fin du P7 mais je rencontre un dernier problème que je n'arrive pas à résoudre (enfin 2 problèmes mais qui sont quasiment identiques).
    Sur la page qui affiche le fils d'actualité j'ai créé une boucle pour afficher tout les posts présent dans la base de données et dans le template de cette boucle il y a un input pour écrire des commentaires. Mais quand j'écris dans un des inputs ça écrit simultanément dans tous les champs en même temps. Par contre quand je poste le commentaire tout va bien, ça s'affiche bien sur celui que je veux.
    Pour le 2ème problème c'est donc à peu près la même chose, j'ai un "bouton" qui permet d'afficher tout les commentaires d'un post mais si je clique sur un ça affiche les commentaires de tout les posts alors que bien évidemment je veux seulement que ça affiche les commentaires du post sur lequel j'ai cliqué.
    Je sais pas si clair ce que j'explique mais une image seras peut-être plus parlante.

    Pour le code ça donne ça, 
    <!-- section wall -->
          <div class="allPosts card col-lg-7 col-xl-5" v-for="post in posts" :key="post.id">
            <div class="allPosts__header">
              <img @click="toProfile(post.User.id)" class="imgProfile" :src="post.User.imageUrl" alt="photo de profil">
              <div class="allPosts__infos">
                <h2> {{ post.User.nom }} {{ post.User.prenom }}</h2>
                <p>posté le {{ formatDate(post.createdAt) }}</p>
              </div>
              <div v-if="currentUser.userId == post.User.id || user.isAdmin" @click="deletePost(post.id)" class="deletePost">
                <i class="deleteCrossPost fa-solid fa-xmark"></i>
              </div>
            </div>
            <div class="allPosts__title">
              <h3> {{ post.content }}</h3>
            </div>
              <img v-if="post.imageURL!= null" class="allPosts__photo" :src="post.imageURL" alt="photo de publication">
              <!-- section commentaire -->
            <div class="allPosts__comment">
              <div class="allPosts__publish">
                <input class="allPosts__input" v-model.trim="contentComment" type="text" placeholder="Ecrivez un commentaire...">
                <button @click="newComment(post.id)" class="btn btn-primary">Commenter</button>
              </div>
              <div class="allPosts__showComment">
                <p @click="showComments()" class="showComment">Afficher les commentaires  ({{post.Comments.length}})</p>
              </div>
              <div v-show="hiddenComment" v-for="comment in comments" :key="comment.id">
                <div class="comment" v-if="post.id == comment.Post.id">
                  <div class="comment__imgProfile">
                    <img @click="toProfile(comment.User.id)" class="imgProfile" :src="comment.User.imageUrl" alt="photo de profil">
                  </div>
                  <div class="comment__content">
                    <div class="comment__nomEtComment">
                      <div class="comment__name">
                        {{ comment.User.nom }} {{ comment.User.prenom }}
                      </div>
                      <div class="comment__comment">
                        {{ comment.comment }}
                      </div>
                    </div>
                    <div class="comment__date">
                      Posté le {{ formatDate(comment.createdAt) }}
                    </div>
                  </div>
                  <div v-if="currentUser.userId == comment.User.id || user.isAdmin" @click="deleteComment(comment.id)">
                    <i class="deleteCrossComment fa-solid fa-xmark"></i>
                  </div>
                </div>
              </div>
            </div>
          </div>
        </div>
    si qqun a une solution ça m'aiderai beaucoup, merci.
    • Partager sur Facebook
    • Partager sur Twitter
      13 mai 2022 à 19:48:04

      Bonjour,

      Il est possible que tu aies déjà trouvé et corrigé ton problème depuis le temps mais au cas où...

      1) Tu as oublié de préciser le langage/framework utilisé dans ton code. Pense-y, car sinon beaucoup de gens ne pourront pas t'aider s'ils ne savent même pas quel langage tu utilises. A vue d'oeil, il me semble reconnaître malgré tout du Vue.js, non ?

      2) Tu nous donnes ton HTML, mais pas ton script. Pourtant, c'est ton script qui permet de gérer toute la partie dynamique de ton site/application. Sans ton script, c'est difficile de savoir tout ce que tu as pu écrire dans ton code pour gérer le remplissage de ton formulaire par tes utilisateurs. Tu nous obliges à deviner :)

      3) Je ne suis pas très bon dans Vue.Js mais je pense que tu as commis une erreur dans la conception de ton code. Tu gères l'ensemble de tes posts et de leurs commentaires avec des boucles, de ce que je peux voir dès ta ligne 2. Mais du coup, il faut faire attention car chaque élément HTML de ta boucle partagera les mêmes id, class et model éventuels que tu lui auras attribué avec tous les autres éléments de cette même boucle que tu as créé à la ligne 2. 

      Du coup, comme tes input ont tous le même nom v-model à la ligne 20, c'est normal qu'un commentaire écrit dans un input le rajoute aussi dans un autre input dont le v-model porte le même nom. Si tu lis la documentation de Vue.js, tu verras que v-model a justement été créé pour cela. Si ton commentaire une fois créé apparait seulement au bon endroit lorsqu'il est posté, c'est sans doute grâce à ton back-end. Dans ta fonction, côté back-end, tu as dû écrire une fonction où le commentaire posté ne doit être attribué qu'à un post déterminé et identifié par un Id.

      Par exemple, côté front-end, tu as sans doute écrit quelque chose comme :

      const oneComment = this.contentComment; //la valeur enregistrée dans le data de ton script puis modifiée par l'utilisateur dans ton v-model
      const idPost =  ... //l'id du post en question, je ne sais pas comment tu le récupères
      
      requestOptions = {
      method: 'POST',
      headers: 'Content-Type': 'application/json',
      body: JSON.stringify({'Comment': oneComment, 'Id': idPost})
      }
      
      fetch(/*la route vers ton API*/, requestOptions)
      .then(...)
      .catch(...);

      alors que côté back-end, tu as dû écrire quelque chose comme :

      //si tu utilises sequelize au lieu de directement MySQL, tu exportes depuis ton fichier comment.controller vers ton fichier comment.route:
      exports.postComment = (req, res) => {
          //...
          const comment = new Comment ({
              text: req.body.Comment,
              idPost: req.body.Id,
              //les autres infos de ton modèle
          })
          /*si tu as un modèle de commentaire pour tes posts dont le nom est "Comment", le texte du commentaire "text", et le post auquel il doit se rattacher "idPost"*/
          comment.save()
          .then(...)
          .catch(...)
          //...
      }

      Encore une fois je ne fais que deviner car je ne connais pas du tout ton script front-end où tes fonctions back-end, mais si tu t'y es pris globalement comme ça, c'est le fait que tu aies attribué le texte du commentaire uniquement à un post déterminé et identifié par son Id côté back-end qui te permet ensuite de faire apparaitre le commentaire posté uniquement en-dessous du bon post dans le front-end lorsque tu veux afficher le commentaire.

      Puis, pour ton 2è problème, c'est sans doute la même chose. Encore une fois à cause de ta boucle avec les mêmes noms, une action sur un élément de la boucle de répercute forcément sur les autres.

      Du coup, je vois 2 solutions:

      1) tu prévois un nom différent à chaque fois pour chaque élément de ta boucle

      2) tu évites de d'utiliser une boucle. Et du coup tu pourrais faire un peu comme Twitter, où les commentaires ne peuvent pas être affichés et postés dès la page page principale du profil que tu consultes, mais tu dois clique d'abord sur un post déterminé pour ensuite pouvoir rajouter ou consulter les commentaires. Et du coup, les "id" de tes posts ne seraient pas récupérés depuis le ":key" que tu utilises dans ton HTML, mais depuis le paramètre URL, par exemple "http://localhost.../groupomania/post?id=1"

      Bon, c'est juste des idées :) je ne connais pas grand chose aux réseaux sociaux et je maîtrise encore moins Vue.Js donc tu trouveras sûrement de meilleures idées que moi. Et sinon, si je me suis trompé et que les problèmes ne venaient pas du tout de là, il faudra que tu posts ton code javascript parce que ton HTML n'est pas suffisant pour identifier la source du problème.

      Bon courage !

      • Partager sur Facebook
      • Partager sur Twitter
        19 mai 2022 à 16:28:24

        Bonjour,

        Tout d'abord merci pour ta réponse et désolé pour le temps que j'ai mis pour la mienne. À vrai je me suis un peu absenté ces dernier temps, donc non je n'ai malheureusement pas encore résolu mon problème.

        Merci également pour tes conseils. 

        Effectivement c'est bien du Vue.js.

        Pour ce qui de mon script le voila : 

        showComments() {
              this.hiddenComment = !this.hiddenComment;
        },
        newComment(id) {
              if (this.contentComment == "") {
                this.errorContent = true;
              } else {
                axios.post(`http://localhost:3000/api/post/${id}/comment`, {
                  comment: this.contentComment
                }, {
                  headers: {
                  "Content-Type": "application/json",
                  "Authorization": `Bearer ${this.currentUser.token}` 
                }
                }).then((response) => {
                this.contentComment= "";
                this.getPosts();
                console.log(response.data);
                }).catch((error) => {
                  console.log(error);
                });
              }
        },
        getComments() {
           axios.get("http://localhost:3000/api/post/comment", {
                headers: {
                  "Content-Type": "application/json",
                  "Authorization": `Bearer ${this.currentUser.token}` 
                }
              })
              .then((res) => {
                this.comments = res.data;
                console.log(res.data);
              })
        },

        J'aurais aimé pouvoir ciblé un input en particulier tout en le laissant dans la boucle.

        Je dois dire que tourner en rond c'est démoralisant 😅

        • Partager sur Facebook
        • Partager sur Twitter
          22 mai 2022 à 16:30:04

          Bonjour,

          Il n'y a pas de soucis. Je comprends pour la perte de moral, mais dis-toi que c'est ton dernier projet. Mais je comprends! Bon courage !

          Sinon pour tes problèmes, je pense qu'ils sont faciles à cerner. C'est juste que tu manques de recul sur ton propre code, je pense. Conseil, quand tu lis ton code, lis ce qui est écrit ligne par ligne et explique-toi à l'oral ce que chaque ligne de code fait et non pas ce que tu veux que chaque ligne de code fasse. C'est un excellent moyen de débugguer, et je pense que tu verrais toi-même ce qui ne va pas.

          Sinon, moi je suis allé sur ton github parce que ce que tu postes juste ici ne suffisait pas. Juste une remarque, dans tes fonctions, côté back-end, j'ai remarqué que tu utilisais énormément "let" pour des variables qui ne changeaient jamais de valeur. Autant utiliser "const", nan ? 

          Et "J'aurais aimé pouvoir ciblé un input en particulier tout en le laissant dans la boucle.", désolé, mais je n'ai pas compris cette phrase.

          Sinon... voilà ce que je pense :

          1) la raison pour laquelle tes commentaires s'affichent sur tous les posts :

          c'est exactement comme je te l'avais dit la première fois. C'EST A CAUSE DE TES V-MODELS !!! Lis la documentation de Vue.js et tu comprendras. On appelle ça le 2-way-binding. Tu donnes un nom de variable à un élément dans tes data de ton script, et tu lis cet élément à ton DOM par le v-model. Les 2 sont liés, donc dès que la valeur de l'un est changé, l'autre est changé avec. Exemple:

           _ _ _ _ _ _ _ 

          (DOM)

          v-model-1=""

          v-model-2=""

          v-model-3=""

          (SCRIPT, DATA)

          élement-1=""

          _ _ _ _ _ 

          Consiqère que l'lément 1 est les 3 v-model aient tous le même nom. A la base, lorsque le composant s'affiche, il n'y a pas de valeur dans les v-model, mais il y en a une dans élément-1, et c'est un string vide. Donc la valeur des v-model par défaut est également un string vide. Puis, l'utilisateur modifie la valeur de v-model-1 en écrivant un commentaire. Automatiquement, les v-model 2 et 3 changent de valeur, et aussi le élément-1. Quelle valeur prennent-ils ? La même valeur que v-model-1 puisque les 4 éléments sont liés car ils ont tous le même nom ! ILS ONT TOUJOURS LA MEME VALEUR DONC MODIFIER UN C'EST MODIFIER TOUT ! C'est pour ça que lorsque l'utilisateur écrit dans le commentaire du premier post, le commentaire apparait aussi dans les autres posts, c'est parfaitement normal. Je t'ai proposé 2 solutions la dernière fois.

          1-tu choisis un v-model différent pour le commentaire de chaque post (trop compliqué pour peu de résultat, je te le déconseille).

          2-comme Twitter, tu empêches les utilisateurs de commenter depuis la page principale et les force à se diriger vers une page affichant le post concerné et lui seul. Un seul post donc un seul v-model, et donc moins de problème pour toi à gérer tout ça. C'est la meilleure solution possible et facile à coder selon moi.

          3-une troisième solution que je te propose, mais beaucoup plus compliquée, et je ne suis même pas sûr que ça fonctionne car je n'ai pas testé ce code avant de te le proposer, mais toi tu peux l'essayer bien sûr, c'est de ne pas passer par ton v-model du tout. L'idée que j'ai en tête :

          newComment(id) {
                if (this.contentComment == "") {
                  this.errorContent = true;
                } else {
          const InputsForComments = document.getElementsByClassName('allPosts__input'); /*renvoie un tableau contenant tous les éléments du DOM avec la class "allPosts__Input", donc les inputs pour les commentaires*/
          const longerInputWithValue = InputsForComments.reduce((firstInput, secondInput) => Math.max(firstInput.value.length, secondInput.value.length), 1); /*récupère le tableau InputForComments puis compare chaque Input et conserve uniquement l'input dont la longueur de sa valeur (en nombres de caractères) est la plus longue*/
          const indexOfInputWithValue = InputsForComments.indexOf(longerInputWithValue); /*trouve l'index de l'input dont le string est le plus long dans le tableau InputsForComments*/
          const commentInTheInputWithValue = InputsForComments[indexOfInputWithValue].value; /*récupère dans le tableau InputsForComments la valeur de l'input avec sa valeur la plus longue(celle où le commentaire a été écrit)*/
          
                  axios.post(`http://localhost:3000/api/post/${id}/comment`, {
                    comment: commentInTheInputWithValue /*le commentaire de l’utilisateur*/
                  }, {
                    headers: {
                    "Content-Type": "application/json",
                    "Authorization": `Bearer ${this.currentUser.token}`
                  }
                  }).then((response) => {
                  this.contentComment= "";
                  this.getPosts();
                  console.log(response.data);
                  }).catch((error) => {
                    console.log(error);
                  });
                }
          }
          

          Mais franchement, je ne suis pas sûr que ça fonctionne, et de toute façon l'utilisateur peut facilement casser ta fonction. Par exemple, s'il écrit plusieurs commentaires avec la même longueur de chaine de caractère dans plusieurs inputs différents, ta méthode ".reduce" ne pourra pas fonctionner vu qu'elle doit renvoyer une valeur unique mais que plusieurs valeurs correspondent à la condition que tu as passé avec ta fonction math.max... autrement dit c'est franchement galère ! Il y a peut-être un moyen beaucoup plus simple de développer ce genre de fonctionnalité mais je n'en ai pas une en tête pour l'instant en tout cas. Pour moi, le réseau social version Twitter est largement plus simple et t'évite énormément de problèmes !

          2) pourquoi le commentaire n'apparait que dans le post correspondant:

          Ca aussi j'y ai déjà répondu. C'est ta fonction dans le back-end qui te sauve la mise, tout simplement. Dans ton code que j'ai recopié ci-dessous, le commentaire n'est attribué qu'à un post déterminé en fonction de son Id. C'est aussi simple que cela.

          exports.createComment = async (req, res) => {
            let headerAuth = req.headers['authorization'];
            let userId = jwtUtils.getUserId(headerAuth);
            let postId = req.params.id
          
            let comment = req.body.comment;
          
            models.User.findOne({
              where: { id: userId }
            })
              .then(userFound => {
                if (userFound !== null) {
          
                  models.Post.findByPk(postId)
                    .then(postFound => {
                      models.Comment.create({
                        UserId: userFound.id,
                        PostId: postFound.id,
                        comment: comment,
                      })
                        .then((newComment) => {
                          res.status(201).json(newComment)
                        }).catch((error) => {
                          res.status(500).json(error)
                        })
                    }).catch(err => { err })
                }
              }).catch(error => {
                return res.status(500).json({ error: 'unable to verify user' })
              });
          };

          3) Pourquoi les commentaires s'affichent sous tous les posts et pas juste celui pour qui le bouton a été cliqué ?

          C'est à cause de ta data "hiddenComment". A la base, tu l'attribues à "false" donc tous les commentaires sont cachés. Mais lorsque l'utilisateur clique sur le bouton, "hiddenComment" passe à "true". Ok, SAUF QU'IL PASSE A "TRUE" POUR TOUS LES POSTS puisque ta data "hiddenComment" est commune à toutes les itérations de ta boucle. Il suffit donc que l'utilisateur clique sur un seul bouton pour que tous les commentaires de tous les posts passent de "false" (ne pas être affichés) à "true" (être affichés).

          Donc là 2 solutions, soit tu fais en sorte que "hiddenComment" passe à "true" uniquement pour le post dont l'utilisateur veut voir les commentaires et pas les autres (bon courage...) soit et c'est beaucoup plus simple, tu te contentes de créer une fonction qui sera appelée lorsque l'utilisateur clique sur le bouton "afficher les commentaires", et cette fonction doit afficher sous le post correspondant les commentaires qui seront tous reliés à ce fameux post correspondant dans ta base données (par un Id, normalement). Et là c'est beaucoup plus facile à coder, tu devrais trouver facilement. 

          Voilà normalement, c'est tout. Pense à commenter davantage ton code afin de pouvoir être compris plus facilement par les autres développeurs.

          Encore une fois, bon courage !

          -
          Edité par NathanaëlMarie-Louise 22 mai 2022 à 22:34:09

          • Partager sur Facebook
          • Partager sur Twitter

          v-model dans un boucle

          × Après avoir cliqué sur "Répondre" vous serez invité à vous connecter pour que votre message soit publié.
          • Editeur
          • Markdown