Bonjour,

Voila je rencontre un petit problème avec mon code.

Ce que je fais

J'utilise Laravel 5.4 et VueJS 2, je fais une requête ajax pour récupéer une liste de gallerie photo, j'ai une structure html particulière : je veux placer chaque gallerie dans un code html précis, je ne souhaite donc pas faire de boucle car le html ne se répète pas, donc dans ma vue, pour récupérer mes infos, j'écris le code comme ceci: {{ galleries[0].id }} et je récupére une erreur "Cannot read property 'id' of undefined". Ce que je ne comprend pas, c'est si je fais {{ galleries[0] }} il affiche correctement l'index et sa valeur, mais dès que je veux rentrer dans l'objet, j'ai cette erreur. Je ne comprend pas mon erreur, je ne pense pas que ce soit ma syntaxe après avoir vérifier sur W3school et surtout, quand je teste sur jsfiddle, ça semble fonctionner : https://jsfiddle.net/fredlab/k6pn17xq/2/

<template>
    <section class="preview">
        <div class="focus">
            <p>gallery  {{ galleries[0].id }}</p>
        </div>
     </section>
</template>

<script type="text/babel">
    export default {
        data() {
            return {
                galleries: []
            }
        },
        mounted() {
            axios.get('/preview').then( ({data}) => this.galleries = data)
        }
    }
</script>

Ce que je veux

Je veux pouvoir accéder aux données de mon tableau JSON dans ma vue sans devoir faire de boucle et placer chaque élément comme je veux dans ma grille HTML

Ce que j'obtiens

J'obtiens cette erreur : "Cannot read property 'id' of undefined" dès que j'essaye d'accéder aux valeurs du tableau JSON comme ceci, pour l'index 1 {{ galleries[0].pictures[0].title }}
Je ne vois pas où je me trompe, et le fait d'y arriver sur jsfiddle me perturbe encore plus.

12 réponses


Maenhyr
Réponse acceptée

Dans ton exemple avec le v-if, ta condition est incorrecte. Tu lui dis d'afficher l'id du premier element de galleries si galleries existe. Hors un tableau vide rempli cette condition. ta condition doit donc etre galleries.length > 0;

Ceci fonctionne correctement pour moi.

<script src="https://unpkg.com/vue/dist/vue.js"></script>
<div id="app">
  <main-preview></main-preview>
</div>
Vue.component('main-preview', {
  template: "<div v-if='galleries.length > 0'> <p>Galleries : {{ galleries[0].id }}</p></div>", // on s'assure d'avoir au moins un element
  data: function() {
    return {
      galleries: []
    }
  },
  mounted() {
    // j'utilise une Promise pour simuler ton appel AJAX
    Promise.resolve([{
        "id": 73
      }, {
        "id": 72
      }, {
        "id": 71
      }])
      .then((galleries) => this.galleries = galleries)
  }

})

var app = new Vue({
  el: '#app'
})

Je ne connais pas le fonctionnement de Vue.js mais il est clair que le problème vient de l'asynchronisme. Ton exemple fonctionne sur jsFiddle car tu lui fournis la valeur dans ta méthode data(). Hors voici le comportement de ton code :

1) je lance la fonction data() et je récupère galleries = [];
2) je compile le template
3) je lance la fonction mounted()
4) j'attends les résultats de ma requête
5) je recompile le temple avec les données de la requête

Le problème survient lors du point 2. Ton tableau est vide donc tu ne peux pas avoir d'id. Il te faut faire une condition pour voir si tu as quelque chose dans ce tableau.

edel
Auteur

Le truc, c'est que dans la console (en galérant un peu pour retrouver l'info) ou avec Vue Dev-tools, galleries n'est pas vide et contient les valeurs du JSON. De plus, si je fais {{ galleries[0] }} il m'affiche bien un objet contenant les valeurs de l'index 0. L'erreur se produit quand je souhaite accéder à ces valeurs (par exemple, {{ galleries[0].id }} )

Je pense que c'est tout simplement une histoire de temps. Ce que tu console.log est le résultat de ton état après avoir eu les résultats de ta requête. Seulement ton code s'exécute entre temps, même si cela n'est pas visible à l'oeil nu. Le problème se passe entre le moment où tu déclares galleries comme une tableau vide et que tu veux afficher l'id de la première valeur de ton tableau.

Je ferai quelque chose comme ceci :

<template>
    <section class="preview">
        <div class="focus" v-if="galleries[0]">
            <p>gallery  {{ galleries[0].id }}</p>
        </div>
     </section>
</template>

Du coup tu n'affiches ton <p> que si tu as une valeur dans ton tableau.

edel
Auteur

Je vois ce que tu veux dire, mais ce que je ne comprend pas, c'est pourquoi je peux afficher galleries[0] et pas galleries[0].id, dans le premier cas, la vue serait bien chargé et pas dans le second cas ?? Le but de mettre la requête AJAX dans mounted() c'est justement pour être sûr que la requête soit faite quand la vue est bien monté.
On est bien d'accord qu'il n'y a pas d'erreur de syntaxe en écrivant ceci : galleries[0).id ??
Je n'ai pas ré inventé la roue, mettre la requête AJAX dans mounted() c'est ce que j'ai lu sur plusieurs tuto (laracast et grafikart)
Je pensais aussi à un soucis avec webpack/mix mais je n'ai rien touché à cette partie de l'application.
Je seche complétement sur ce coup-là.

EDIT : j'ai modifié le jsfiddle pour prendre en compte l'étape mounted() https://jsfiddle.net/k6pn17xq/3/ et cela reproduit mon soucis, je peux accéder à galleries[0] mais pas galleries[0].id

je peux afficher galleries[0] et pas galleries[0].id

C'est normal. Lors du premier compile, tu as undefined donc l'élement n'est pas affiché ou tu as un <p> vide à l'écran. Ensuite ta requête se fait et du coup tu as quelque chose dans galleries[0] donc ton élément s'affiche.

Maintenant si tu fais galleries[0].id tu auras une erreur de compilation à la première exécution vu que galleries est un tableau vide. Du coup le reste du code n'est pas lancé. Le problème ne vient pas du mounted(). Il vient du fait que tu veux afficher un id d'un élément qui n'existe pas entre data() et mounted().

edel
Auteur

Je viens te tester ta proposition dans jsfidle : https://jsfiddle.net/k6pn17xq/4/ ça semble fonctionner. J'attend ce soir pour tester chez moi dans mon appli pour valider le sujet, mais si ça marche, tu m'as littéralement sauvé la vie :)
Un énorme merci pour ton aide.

Edit : en modifiant l'évement de mounted() à created(), cela semble fonctionner aussi : https://jsfiddle.net/k6pn17xq/5/ ça me laisse 2 solutions à tester ce soir (https://vuejs.org/v2/guide/instance.html#Lifecycle-Diagram)

edel
Auteur

Joie de courte durée : aucune des 2 solutions ne fonctionnent, toujours des erreurs "Cannot read property 'id' of undefined".
:(

peux tu afficher ton nouveau code stp ?

edel
Auteur

Avec la solution "created()"

<template>
    <section class="preview">
        <div class="focus">
            <p>gallery  {{ galleries[0].id }}</p>
        </div>
     </section>
</template>

<script type="text/babel">
    export default {
        data() {
            return {
                galleries: []
            }
        },
       created {
            axios.get('/preview').then( ({data}) => this.galleries = data)
        }
    }
</script>

avec la solution du v-if :

<template>
    <section class="preview">
        <div class="focus" v-if="galleries">
            <p>gallery  {{ galleries[0].id }}</p>
        </div>
     </section>
</template>

<script type="text/babel">
    export default {
        data() {
            return {
                galleries: []
            }
        },
        created() {
            axios.get('/preview').then( ({data}) => this.galleries = data)
        }
    }
</script>

Je me demande si c'est pas un soucis de webpack

edel
Auteur

ok, merci pour ta réponse, je test ça ce soir chez moi. Ici, je n'ai pas accès à mon code.

edel
Auteur

Je viens de tester et cela fonctionne, j'ai le comportement que je souhaite. Un énorme merci pour ton aide, je n'aurais pas trouvé tout seul.