Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Nuxt JS Apollo data only available after page refresh

I am fetching some data using Apollo inside of Nuxt. Somehow, when navigating to that page I get an error of

Cannot read property 'image' of undefined

When I refresh the page, everything works as expected.

I have a found a few threads of people having similar issues but no solution seems to work for me :/

This is my template file right now:

/products/_slug.vue

<template>
  <section class="container">
    <div class="top">
      <img :src="product.image.url"/>
      <h1>{{ product.name }}</h1>
    </div>
  </section>
</template>

<script>
import gql from 'graphql-tag'

export default {
  apollo: {
    product: {
      query: gql`
        query Product($slug: String!) {
          product(filter: { slug: { eq: $slug } }) {
            slug
            name
            image {
              url
            }
          }
        }
      `,
      prefetch({ route }) {
        return {
          slug: route.params.slug
        }
      },
      variables() {
        return {
          slug: this.$route.params.slug
        }
      }
    }
  }
}
</script>

Basically the $apolloData stays empty unless I refresh the page. Any ideas would be much appreciated

EDIT Got one step closer (I think). Before, everything (image.url and name) would be undefined when navigating to the page for the first time.

I added:

data() {
    return {
      product: []
    };
  }

at the top of my export and now at least the name is always defined so if I remove the image, everything works as expected. Just the image.url keeps being undefined.

One thing I noticed (not sure how relevant) is that this issue only occurs using the , if I use a normal a tag it works but of course takes away the vue magic.

EDIT-2 So somehow if I downgrade Nuxt to version 1.0.0 everything works fine

like image 469
Martin Conde Avatar asked Mar 18 '19 00:03

Martin Conde


3 Answers

I stumbled on this issue as well, and found it hidden in the Vue Apollo documents.

Although quite similar to the OP's reply, it appears the official way is to use the "$loadingKey" property.

It's quite confusing in the documents because there are so many things going on. https://vue-apollo.netlify.com/guide/apollo/queries.html#loading-state

<template>
  <main
    v-if="!loading"
    class="my-8 mb-4"
  >
    <div class="w-3/4 mx-auto mb-16">
      <h2 class="mx-auto text-4xl text-center heading-underline">
        {{ page.title }}
      </h2>
      <div
        class="content"
        v-html="page.content.html"
      ></div>
    </div>
  </main>
</template>
    
<script>
import { page } from "~/graphql/page";
    
export default {
  name: 'AboutPage',

  data: () => ({
    loading: 0
  }),
    
  apollo: {
    $loadingKey: 'loading',
    page: {
      query: page,
      variables: {
        slug: "about"
      }
    },
  }
}
</script>

If you need to use a reactive property within vue such as a slug, you can do so with the following.

<template>
  <main
    v-if="!loading"
    class="my-8 mb-4"
  >
    <div class="w-3/4 mx-auto mb-16">
      <h2 class="mx-auto text-4xl text-center heading-underline">
        {{ page.title }}
      </h2>
      <div
        class="content"
        v-html="page.content.html"
      ></div>
    </div>
  </main>
</template>
    
<script>
import { page } from "~/graphql/page";
    
export default {
  name: 'AboutPage',
    
  data: () => ({
    loading: 0
  }),
    
  apollo: {
    $loadingKey: 'loading',
    page: {
      query: page,
      variables() {
        return {
          slug: this.$route.params.slug
        }
      }
    },
  }
}
</script>
like image 85
Rob Mellett Avatar answered Nov 18 '22 04:11

Rob Mellett


To fix this, you add v-if="!$apollo.loading" to the HTML container in which you're taying to use a reactive prop.

like image 26
Nadir Abbas Avatar answered Nov 18 '22 06:11

Nadir Abbas


I think it's only a problem of timing on page load.

You should either iterate on products, if you have more than one, or have a v-if="product != null" on a product container, that will render only once the data is fetched from GraphQL.

In that way you'll use the object in your HTML only when it's really fetched and avoid reading properties from undefined.

like image 4
mat_jack1 Avatar answered Nov 18 '22 04:11

mat_jack1